@tsparticles/shape-image 3.0.0-alpha.1 → 3.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -12
- package/browser/GifUtils/ByteStream.js +44 -0
- package/browser/GifUtils/Constants.js +2 -0
- package/browser/GifUtils/Enums/DisposalMethod.js +1 -0
- package/browser/GifUtils/Types/ApplicationExtension.js +1 -0
- package/browser/GifUtils/Types/Frame.js +1 -0
- package/browser/GifUtils/Types/GIF.js +1 -0
- package/browser/GifUtils/Types/GIFDataHeaders.js +1 -0
- package/browser/GifUtils/Types/GIFProgressCallbackFunction.js +1 -0
- package/browser/GifUtils/Types/PlainTextData.js +1 -0
- package/browser/GifUtils/Utils.js +324 -0
- package/browser/ImageDrawer.js +122 -58
- package/browser/ImagePreloader.js +33 -0
- package/browser/Options/Classes/Preload.js +29 -0
- package/browser/Options/Interfaces/IPreload.js +1 -0
- package/browser/Utils.js +35 -9
- package/browser/index.js +42 -2
- package/browser/types.js +1 -0
- package/cjs/GifUtils/ByteStream.js +48 -0
- package/cjs/GifUtils/Constants.js +5 -0
- package/cjs/GifUtils/Enums/DisposalMethod.js +2 -0
- package/cjs/GifUtils/Types/ApplicationExtension.js +2 -0
- package/cjs/GifUtils/Types/Frame.js +2 -0
- package/cjs/GifUtils/Types/GIF.js +2 -0
- package/cjs/GifUtils/Types/GIFDataHeaders.js +2 -0
- package/cjs/GifUtils/Types/GIFProgressCallbackFunction.js +2 -0
- package/cjs/GifUtils/Types/PlainTextData.js +2 -0
- package/cjs/GifUtils/Utils.js +329 -0
- package/cjs/ImageDrawer.js +124 -71
- package/cjs/ImagePreloader.js +37 -0
- package/cjs/Options/Classes/Preload.js +33 -0
- package/cjs/Options/Interfaces/IPreload.js +2 -0
- package/cjs/Utils.js +66 -52
- package/cjs/index.js +42 -13
- package/cjs/types.js +2 -0
- package/esm/GifUtils/ByteStream.js +44 -0
- package/esm/GifUtils/Constants.js +2 -0
- package/esm/GifUtils/Enums/DisposalMethod.js +1 -0
- package/esm/GifUtils/Types/ApplicationExtension.js +1 -0
- package/esm/GifUtils/Types/Frame.js +1 -0
- package/esm/GifUtils/Types/GIF.js +1 -0
- package/esm/GifUtils/Types/GIFDataHeaders.js +1 -0
- package/esm/GifUtils/Types/GIFProgressCallbackFunction.js +1 -0
- package/esm/GifUtils/Types/PlainTextData.js +1 -0
- package/esm/GifUtils/Utils.js +324 -0
- package/esm/ImageDrawer.js +122 -58
- package/esm/ImagePreloader.js +33 -0
- package/esm/Options/Classes/Preload.js +29 -0
- package/esm/Options/Interfaces/IPreload.js +1 -0
- package/esm/Utils.js +35 -9
- package/esm/index.js +42 -2
- package/esm/types.js +1 -0
- package/package.json +6 -5
- package/report.html +4 -4
- package/tsparticles.shape.image.js +676 -73
- package/tsparticles.shape.image.min.js +1 -1
- package/tsparticles.shape.image.min.js.LICENSE.txt +1 -8
- package/types/GifUtils/ByteStream.d.ts +11 -0
- package/types/GifUtils/Constants.d.ts +2 -0
- package/types/GifUtils/Enums/DisposalMethod.d.ts +10 -0
- package/types/GifUtils/Types/ApplicationExtension.d.ts +5 -0
- package/types/GifUtils/Types/Frame.d.ts +19 -0
- package/types/GifUtils/Types/GIF.d.ts +16 -0
- package/types/GifUtils/Types/GIFDataHeaders.d.ts +9 -0
- package/types/GifUtils/Types/GIFProgressCallbackFunction.d.ts +2 -0
- package/types/GifUtils/Types/PlainTextData.d.ts +11 -0
- package/types/GifUtils/Utils.d.ts +4 -0
- package/types/IImageShape.d.ts +2 -1
- package/types/ImageDrawer.d.ts +9 -9
- package/types/ImagePreloader.d.ts +10 -0
- package/types/Options/Classes/Preload.d.ts +12 -0
- package/types/Options/Interfaces/IPreload.d.ts +8 -0
- package/types/Utils.d.ts +15 -5
- package/types/index.d.ts +2 -2
- package/types/types.d.ts +17 -0
- package/umd/GifUtils/ByteStream.js +58 -0
- package/umd/GifUtils/Constants.js +15 -0
- package/umd/GifUtils/Enums/DisposalMethod.js +12 -0
- package/umd/GifUtils/Types/ApplicationExtension.js +12 -0
- package/umd/GifUtils/Types/Frame.js +12 -0
- package/umd/GifUtils/Types/GIF.js +12 -0
- package/umd/GifUtils/Types/GIFDataHeaders.js +12 -0
- package/umd/GifUtils/Types/GIFProgressCallbackFunction.js +12 -0
- package/umd/GifUtils/Types/PlainTextData.js +12 -0
- package/umd/GifUtils/Utils.js +339 -0
- package/umd/ImageDrawer.js +122 -58
- package/umd/ImagePreloader.js +47 -0
- package/umd/Options/Classes/Preload.js +43 -0
- package/umd/Options/Interfaces/IPreload.js +12 -0
- package/umd/Utils.js +37 -10
- package/umd/index.js +43 -3
- package/umd/types.js +12 -0
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
# tsParticles Image Shape
|
|
4
4
|
|
|
5
|
-
[](https://www.jsdelivr.com/package/npm/@tsparticles/shape-image)
|
|
6
|
+
[](https://www.npmjs.com/package/@tsparticles/shape-image)
|
|
7
|
+
[](https://www.npmjs.com/package/@tsparticles/shape-image) [](https://github.com/sponsors/matteobruni)
|
|
8
8
|
|
|
9
9
|
[tsParticles](https://github.com/matteobruni/tsparticles) additional image shape.
|
|
10
10
|
|
|
@@ -26,7 +26,7 @@ Once the scripts are loaded you can set up `tsParticles` and the shape like this
|
|
|
26
26
|
|
|
27
27
|
```javascript
|
|
28
28
|
(async () => {
|
|
29
|
-
await loadImageShape();
|
|
29
|
+
await loadImageShape(tsParticles);
|
|
30
30
|
|
|
31
31
|
await tsParticles.load({
|
|
32
32
|
id: "tsparticles",
|
|
@@ -43,29 +43,33 @@ Once the scripts are loaded you can set up `tsParticles` and the shape like this
|
|
|
43
43
|
This package is compatible also with ES or CommonJS modules, firstly this needs to be installed, like this:
|
|
44
44
|
|
|
45
45
|
```shell
|
|
46
|
-
$ npm install tsparticles
|
|
46
|
+
$ npm install @tsparticles/shape-image
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
or
|
|
50
50
|
|
|
51
51
|
```shell
|
|
52
|
-
$ yarn add tsparticles
|
|
52
|
+
$ yarn add @tsparticles/shape-image
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
Then you need to import it in the app, like this:
|
|
56
56
|
|
|
57
57
|
```javascript
|
|
58
|
-
const { tsParticles } = require("tsparticles
|
|
59
|
-
const { loadImageShape } = require("tsparticles
|
|
58
|
+
const { tsParticles } = require("@tsparticles/engine");
|
|
59
|
+
const { loadImageShape } = require("@tsparticles/shape-image");
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
(async () => {
|
|
62
|
+
await loadImageShape(tsParticles);
|
|
63
|
+
})();
|
|
62
64
|
```
|
|
63
65
|
|
|
64
66
|
or
|
|
65
67
|
|
|
66
68
|
```javascript
|
|
67
|
-
import { tsParticles } from "tsparticles
|
|
68
|
-
import { loadImageShape } from "tsparticles
|
|
69
|
+
import { tsParticles } from "@tsparticles/engine";
|
|
70
|
+
import { loadImageShape } from "@tsparticles/shape-image";
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
(async () => {
|
|
73
|
+
await loadImageShape(tsParticles);
|
|
74
|
+
})();
|
|
71
75
|
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export class ByteStream {
|
|
2
|
+
constructor(bytes) {
|
|
3
|
+
this.pos = 0;
|
|
4
|
+
this.data = new Uint8ClampedArray(bytes);
|
|
5
|
+
}
|
|
6
|
+
getString(count) {
|
|
7
|
+
const slice = this.data.slice(this.pos, this.pos + count);
|
|
8
|
+
this.pos += slice.length;
|
|
9
|
+
return slice.reduce((acc, curr) => acc + String.fromCharCode(curr), "");
|
|
10
|
+
}
|
|
11
|
+
nextByte() {
|
|
12
|
+
return this.data[this.pos++];
|
|
13
|
+
}
|
|
14
|
+
nextTwoBytes() {
|
|
15
|
+
this.pos += 2;
|
|
16
|
+
return this.data[this.pos - 2] + (this.data[this.pos - 1] << 8);
|
|
17
|
+
}
|
|
18
|
+
readSubBlocks() {
|
|
19
|
+
let blockString = "", size = 0;
|
|
20
|
+
do {
|
|
21
|
+
size = this.data[this.pos++];
|
|
22
|
+
for (let count = size; --count >= 0; blockString += String.fromCharCode(this.data[this.pos++])) {
|
|
23
|
+
}
|
|
24
|
+
} while (size !== 0);
|
|
25
|
+
return blockString;
|
|
26
|
+
}
|
|
27
|
+
readSubBlocksBin() {
|
|
28
|
+
let size = 0, len = 0;
|
|
29
|
+
for (let offset = 0; (size = this.data[this.pos + offset]) !== 0; offset += size + 1) {
|
|
30
|
+
len += size;
|
|
31
|
+
}
|
|
32
|
+
const blockData = new Uint8Array(len);
|
|
33
|
+
for (let i = 0; (size = this.data[this.pos++]) !== 0;) {
|
|
34
|
+
for (let count = size; --count >= 0; blockData[i++] = this.data[this.pos++]) {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return blockData;
|
|
38
|
+
}
|
|
39
|
+
skipSubBlocks() {
|
|
40
|
+
for (; this.data[this.pos] !== 0; this.pos += this.data[this.pos] + 1) {
|
|
41
|
+
}
|
|
42
|
+
this.pos++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { InterlaceOffsets, InterlaceSteps } from "./Constants";
|
|
2
|
+
import { ByteStream } from "./ByteStream";
|
|
3
|
+
function parseColorTable(byteStream, count) {
|
|
4
|
+
const colors = [];
|
|
5
|
+
for (let i = 0; i < count; i++) {
|
|
6
|
+
colors.push({
|
|
7
|
+
r: byteStream.data[byteStream.pos],
|
|
8
|
+
g: byteStream.data[byteStream.pos + 1],
|
|
9
|
+
b: byteStream.data[byteStream.pos + 2],
|
|
10
|
+
});
|
|
11
|
+
byteStream.pos += 3;
|
|
12
|
+
}
|
|
13
|
+
return colors;
|
|
14
|
+
}
|
|
15
|
+
async function parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex) {
|
|
16
|
+
switch (byteStream.nextByte()) {
|
|
17
|
+
case 249: {
|
|
18
|
+
const frame = gif.frames[getFrameIndex(false)];
|
|
19
|
+
byteStream.pos++;
|
|
20
|
+
const packedByte = byteStream.nextByte();
|
|
21
|
+
frame.GCreserved = (packedByte & 0xe0) >>> 5;
|
|
22
|
+
frame.disposalMethod = (packedByte & 0x1c) >>> 2;
|
|
23
|
+
frame.userInputDelayFlag = (packedByte & 2) === 2;
|
|
24
|
+
const transparencyFlag = (packedByte & 1) === 1;
|
|
25
|
+
frame.delayTime = byteStream.nextTwoBytes() * 0xa;
|
|
26
|
+
const transparencyIndex = byteStream.nextByte();
|
|
27
|
+
if (transparencyFlag) {
|
|
28
|
+
getTransparencyIndex(transparencyIndex);
|
|
29
|
+
}
|
|
30
|
+
byteStream.pos++;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case 255: {
|
|
34
|
+
byteStream.pos++;
|
|
35
|
+
const applicationExtension = {
|
|
36
|
+
identifier: byteStream.getString(8),
|
|
37
|
+
authenticationCode: byteStream.getString(3),
|
|
38
|
+
data: byteStream.readSubBlocksBin(),
|
|
39
|
+
};
|
|
40
|
+
gif.applicationExtensions.push(applicationExtension);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
case 254: {
|
|
44
|
+
gif.comments.push([getFrameIndex(false), byteStream.readSubBlocks()]);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case 1: {
|
|
48
|
+
if (gif.globalColorTable.length === 0) {
|
|
49
|
+
throw new EvalError("plain text extension without global color table");
|
|
50
|
+
}
|
|
51
|
+
byteStream.pos++;
|
|
52
|
+
gif.frames[getFrameIndex(false)].plainTextData = {
|
|
53
|
+
left: byteStream.nextTwoBytes(),
|
|
54
|
+
top: byteStream.nextTwoBytes(),
|
|
55
|
+
width: byteStream.nextTwoBytes(),
|
|
56
|
+
height: byteStream.nextTwoBytes(),
|
|
57
|
+
charSize: {
|
|
58
|
+
width: byteStream.nextTwoBytes(),
|
|
59
|
+
height: byteStream.nextTwoBytes(),
|
|
60
|
+
},
|
|
61
|
+
foregroundColor: byteStream.nextByte(),
|
|
62
|
+
backgroundColor: byteStream.nextByte(),
|
|
63
|
+
text: byteStream.readSubBlocks(),
|
|
64
|
+
};
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
byteStream.skipSubBlocks();
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) {
|
|
73
|
+
const frame = gif.frames[getFrameIndex(true)];
|
|
74
|
+
frame.left = byteStream.nextTwoBytes();
|
|
75
|
+
frame.top = byteStream.nextTwoBytes();
|
|
76
|
+
frame.width = byteStream.nextTwoBytes();
|
|
77
|
+
frame.height = byteStream.nextTwoBytes();
|
|
78
|
+
const packedByte = byteStream.nextByte(), localColorTableFlag = (packedByte & 0x80) === 0x80, interlacedFlag = (packedByte & 0x40) === 0x40;
|
|
79
|
+
frame.sortFlag = (packedByte & 0x20) === 0x20;
|
|
80
|
+
frame.reserved = (packedByte & 0x18) >>> 3;
|
|
81
|
+
const localColorCount = 1 << ((packedByte & 7) + 1);
|
|
82
|
+
if (localColorTableFlag) {
|
|
83
|
+
frame.localColorTable = parseColorTable(byteStream, localColorCount);
|
|
84
|
+
}
|
|
85
|
+
const getColor = (index) => {
|
|
86
|
+
const { r, g, b } = (localColorTableFlag ? frame.localColorTable : gif.globalColorTable)[index];
|
|
87
|
+
return { r, g, b, a: index === getTransparencyIndex(null) ? (avgAlpha ? ~~((r + g + b) / 3) : 0) : 255 };
|
|
88
|
+
};
|
|
89
|
+
const image = (() => {
|
|
90
|
+
try {
|
|
91
|
+
return new ImageData(frame.width, frame.height, { colorSpace: "srgb" });
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error instanceof DOMException && error.name === "IndexSizeError") {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
})();
|
|
100
|
+
if (image == null) {
|
|
101
|
+
throw new EvalError("GIF frame size is to large");
|
|
102
|
+
}
|
|
103
|
+
const minCodeSize = byteStream.nextByte(), imageData = byteStream.readSubBlocksBin(), clearCode = 1 << minCodeSize;
|
|
104
|
+
const readBits = (pos, len) => {
|
|
105
|
+
const bytePos = pos >>> 3, bitPos = pos & 7;
|
|
106
|
+
return (((imageData[bytePos] + (imageData[bytePos + 1] << 8) + (imageData[bytePos + 2] << 16)) &
|
|
107
|
+
(((1 << len) - 1) << bitPos)) >>>
|
|
108
|
+
bitPos);
|
|
109
|
+
};
|
|
110
|
+
if (interlacedFlag) {
|
|
111
|
+
for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pass = 0; pass < 4; pass++) {
|
|
112
|
+
if (InterlaceOffsets[pass] < frame.height) {
|
|
113
|
+
for (let pixelPos = 0, lineIndex = 0;;) {
|
|
114
|
+
const last = code;
|
|
115
|
+
code = readBits(pos, size);
|
|
116
|
+
pos += size + 1;
|
|
117
|
+
if (code === clearCode) {
|
|
118
|
+
size = minCodeSize + 1;
|
|
119
|
+
dic.length = clearCode + 2;
|
|
120
|
+
for (let i = 0; i < dic.length; i++) {
|
|
121
|
+
dic[i] = i < clearCode ? [i] : [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
if (code >= dic.length) {
|
|
126
|
+
dic.push(dic[last].concat(dic[last][0]));
|
|
127
|
+
}
|
|
128
|
+
else if (last !== clearCode) {
|
|
129
|
+
dic.push(dic[last].concat(dic[code][0]));
|
|
130
|
+
}
|
|
131
|
+
for (let i = 0; i < dic[code].length; i++) {
|
|
132
|
+
const { r, g, b, a } = getColor(dic[code][i]);
|
|
133
|
+
image.data.set([r, g, b, a], InterlaceOffsets[pass] * frame.width +
|
|
134
|
+
InterlaceSteps[pass] * lineIndex +
|
|
135
|
+
(pixelPos % (frame.width * 4)));
|
|
136
|
+
pixelPos += 4;
|
|
137
|
+
}
|
|
138
|
+
if (dic.length === 1 << size && size < 0xc) {
|
|
139
|
+
size++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (pixelPos === frame.width * 4 * (lineIndex + 1)) {
|
|
143
|
+
lineIndex++;
|
|
144
|
+
if (InterlaceOffsets[pass] + InterlaceSteps[pass] * lineIndex >= frame.height) {
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
progressCallback?.(byteStream.pos / (byteStream.data.length - 1), getFrameIndex(false) + 1, image, { x: frame.left, y: frame.top }, { width: gif.width, height: gif.height });
|
|
151
|
+
}
|
|
152
|
+
frame.image = image;
|
|
153
|
+
frame.bitmap = await createImageBitmap(image);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pixelPos = -4;;) {
|
|
157
|
+
const last = code;
|
|
158
|
+
code = readBits(pos, size);
|
|
159
|
+
pos += size;
|
|
160
|
+
if (code === clearCode) {
|
|
161
|
+
size = minCodeSize + 1;
|
|
162
|
+
dic.length = clearCode + 2;
|
|
163
|
+
for (let i = 0; i < dic.length; i++) {
|
|
164
|
+
dic[i] = i < clearCode ? [i] : [];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
if (code === clearCode + 1) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
if (code >= dic.length) {
|
|
172
|
+
dic.push(dic[last].concat(dic[last][0]));
|
|
173
|
+
}
|
|
174
|
+
else if (last !== clearCode) {
|
|
175
|
+
dic.push(dic[last].concat(dic[code][0]));
|
|
176
|
+
}
|
|
177
|
+
for (let i = 0; i < dic[code].length; i++) {
|
|
178
|
+
const { r, g, b, a } = getColor(dic[code][i]);
|
|
179
|
+
image.data.set([r, g, b, a], (pixelPos += 4));
|
|
180
|
+
}
|
|
181
|
+
if (dic.length >= 1 << size && size < 0xc) {
|
|
182
|
+
size++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
frame.image = image;
|
|
187
|
+
frame.bitmap = await createImageBitmap(image);
|
|
188
|
+
progressCallback?.((byteStream.pos + 1) / byteStream.data.length, getFrameIndex(false) + 1, frame.image, { x: frame.left, y: frame.top }, { width: gif.width, height: gif.height });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async function parseBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) {
|
|
192
|
+
switch (byteStream.nextByte()) {
|
|
193
|
+
case 59:
|
|
194
|
+
return true;
|
|
195
|
+
case 44:
|
|
196
|
+
await parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback);
|
|
197
|
+
break;
|
|
198
|
+
case 33:
|
|
199
|
+
await parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex);
|
|
200
|
+
break;
|
|
201
|
+
default:
|
|
202
|
+
throw new EvalError("undefined block found");
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
export function getGIFLoopAmount(gif) {
|
|
207
|
+
for (const extension of gif.applicationExtensions) {
|
|
208
|
+
if (extension.identifier + extension.authenticationCode !== "NETSCAPE2.0") {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
return extension.data[1] + (extension.data[2] << 8);
|
|
212
|
+
}
|
|
213
|
+
return NaN;
|
|
214
|
+
}
|
|
215
|
+
export async function decodeGIF(gifURL, progressCallback, avgAlpha) {
|
|
216
|
+
if (!avgAlpha)
|
|
217
|
+
avgAlpha = false;
|
|
218
|
+
const res = await fetch(gifURL);
|
|
219
|
+
if (!res.ok && res.status === 404) {
|
|
220
|
+
throw new EvalError("file not found");
|
|
221
|
+
}
|
|
222
|
+
const buffer = await res.arrayBuffer();
|
|
223
|
+
const gif = {
|
|
224
|
+
width: 0,
|
|
225
|
+
height: 0,
|
|
226
|
+
totalTime: 0,
|
|
227
|
+
colorRes: 0,
|
|
228
|
+
pixelAspectRatio: 0,
|
|
229
|
+
frames: [],
|
|
230
|
+
sortFlag: false,
|
|
231
|
+
globalColorTable: [],
|
|
232
|
+
backgroundImage: new ImageData(1, 1, { colorSpace: "srgb" }),
|
|
233
|
+
comments: [],
|
|
234
|
+
applicationExtensions: [],
|
|
235
|
+
}, byteStream = new ByteStream(new Uint8ClampedArray(buffer));
|
|
236
|
+
if (byteStream.getString(6) !== "GIF89a") {
|
|
237
|
+
throw new Error("not a supported GIF file");
|
|
238
|
+
}
|
|
239
|
+
gif.width = byteStream.nextTwoBytes();
|
|
240
|
+
gif.height = byteStream.nextTwoBytes();
|
|
241
|
+
const packedByte = byteStream.nextByte(), globalColorTableFlag = (packedByte & 0x80) === 0x80;
|
|
242
|
+
gif.colorRes = (packedByte & 0x70) >>> 4;
|
|
243
|
+
gif.sortFlag = (packedByte & 8) === 8;
|
|
244
|
+
const globalColorCount = 1 << ((packedByte & 7) + 1), backgroundColorIndex = byteStream.nextByte();
|
|
245
|
+
gif.pixelAspectRatio = byteStream.nextByte();
|
|
246
|
+
if (gif.pixelAspectRatio !== 0) {
|
|
247
|
+
gif.pixelAspectRatio = (gif.pixelAspectRatio + 0xf) / 0x40;
|
|
248
|
+
}
|
|
249
|
+
if (globalColorTableFlag) {
|
|
250
|
+
gif.globalColorTable = parseColorTable(byteStream, globalColorCount);
|
|
251
|
+
}
|
|
252
|
+
const backgroundImage = (() => {
|
|
253
|
+
try {
|
|
254
|
+
return new ImageData(gif.width, gif.height, { colorSpace: "srgb" });
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
if (error instanceof DOMException && error.name === "IndexSizeError") {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
})();
|
|
263
|
+
if (backgroundImage == null) {
|
|
264
|
+
throw new Error("GIF frame size is to large");
|
|
265
|
+
}
|
|
266
|
+
const { r, g, b } = gif.globalColorTable[backgroundColorIndex];
|
|
267
|
+
backgroundImage.data.set(globalColorTableFlag ? [r, g, b, 255] : [0, 0, 0, 0]);
|
|
268
|
+
for (let i = 4; i < backgroundImage.data.length; i *= 2) {
|
|
269
|
+
backgroundImage.data.copyWithin(i, 0, i);
|
|
270
|
+
}
|
|
271
|
+
gif.backgroundImage = backgroundImage;
|
|
272
|
+
let frameIndex = -1, incrementFrameIndex = true, transparencyIndex = -1;
|
|
273
|
+
const getframeIndex = (increment) => {
|
|
274
|
+
if (increment) {
|
|
275
|
+
incrementFrameIndex = true;
|
|
276
|
+
}
|
|
277
|
+
return frameIndex;
|
|
278
|
+
};
|
|
279
|
+
const getTransparencyIndex = (newValue) => {
|
|
280
|
+
if (newValue != null) {
|
|
281
|
+
transparencyIndex = newValue;
|
|
282
|
+
}
|
|
283
|
+
return transparencyIndex;
|
|
284
|
+
};
|
|
285
|
+
try {
|
|
286
|
+
do {
|
|
287
|
+
if (incrementFrameIndex) {
|
|
288
|
+
gif.frames.push({
|
|
289
|
+
left: 0,
|
|
290
|
+
top: 0,
|
|
291
|
+
width: 0,
|
|
292
|
+
height: 0,
|
|
293
|
+
disposalMethod: 0,
|
|
294
|
+
image: new ImageData(1, 1, { colorSpace: "srgb" }),
|
|
295
|
+
plainTextData: null,
|
|
296
|
+
userInputDelayFlag: false,
|
|
297
|
+
delayTime: 0,
|
|
298
|
+
sortFlag: false,
|
|
299
|
+
localColorTable: [],
|
|
300
|
+
reserved: 0,
|
|
301
|
+
GCreserved: 0,
|
|
302
|
+
});
|
|
303
|
+
frameIndex++;
|
|
304
|
+
transparencyIndex = -1;
|
|
305
|
+
incrementFrameIndex = false;
|
|
306
|
+
}
|
|
307
|
+
} while (!(await parseBlock(byteStream, gif, avgAlpha, getframeIndex, getTransparencyIndex, progressCallback)));
|
|
308
|
+
gif.frames.length--;
|
|
309
|
+
for (const frame of gif.frames) {
|
|
310
|
+
if (frame.userInputDelayFlag && frame.delayTime === 0) {
|
|
311
|
+
gif.totalTime = Infinity;
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
gif.totalTime += frame.delayTime;
|
|
315
|
+
}
|
|
316
|
+
return gif;
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
if (error instanceof EvalError) {
|
|
320
|
+
throw new Error(`error while parsing frame ${frameIndex} "${error.message}"`);
|
|
321
|
+
}
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
}
|