imagescript 1.2.15 → 1.2.17
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/ImageScript.d.ts +233 -223
- package/ImageScript.js +1658 -1646
- package/LICENSE +12 -619
- package/LICENSE.AGPL-3.0 +619 -0
- package/LICENSE.MIT +19 -0
- package/README.md +51 -46
- package/bench/_.mjs +165 -165
- package/bench/all.mjs +3 -3
- package/bench/blur.mjs +58 -58
- package/bench/flip.mjs +62 -62
- package/bench/overlay.mjs +34 -34
- package/bench/package.json +6 -6
- package/bench/readme.md +63 -63
- package/bench/resize.mjs +51 -51
- package/codecs/LICENSE +20 -20
- package/codecs/lib.d.ts +49 -49
- package/codecs/lib.js +5 -5
- package/codecs/node/index.js +2 -2
- package/codecs/node/index.mjs +5 -5
- package/codecs/package.json +13 -13
- package/codecs/readme.md +37 -37
- package/package.json +41 -41
- package/png/node.js +1137 -1137
- package/png/src/crc.mjs +48 -48
- package/png/src/mem.mjs +23 -23
- package/png/src/png.mjs +254 -254
- package/png/src/zlib.mjs +617 -617
- package/tsconfig.json +21 -21
- package/utils/mem.js +25 -25
- package/v2/codecs/magic.js +24 -24
- package/v2/framebuffer.d.ts +66 -66
- package/v2/framebuffer.js +2352 -2352
- package/v2/framebuffer.mjs +125 -125
- package/v2/framebuffer.rs +1263 -1263
- package/v2/index.d.ts +83 -83
- package/v2/index.js +260 -260
- package/v2/ops/blur.mjs +302 -302
- package/v2/ops/color.mjs +276 -276
- package/v2/ops/crop.mjs +63 -63
- package/v2/ops/fill.mjs +32 -32
- package/v2/ops/flip.mjs +32 -32
- package/v2/ops/index.mjs +8 -8
- package/v2/ops/iterator.mjs +33 -33
- package/v2/ops/overlay.mjs +62 -62
- package/v2/ops/resize.mjs +165 -165
- package/v2/ops/rotate.mjs +116 -116
- package/v2/util/mem.mjs +23 -23
- package/wasm/node/font.js +141 -141
- package/wasm/node/gif.js +76 -76
- package/wasm/node/jpeg.js +91 -91
- package/wasm/node/png.js +49 -49
- package/wasm/node/svg.js +55 -55
- package/wasm/node/tiff.js +46 -46
package/v2/index.js
CHANGED
|
@@ -1,261 +1,261 @@
|
|
|
1
|
-
const mem = require('./util/mem.js');
|
|
2
|
-
const png = require('../png/node.js');
|
|
3
|
-
const magic = require('./codecs/magic.js');
|
|
4
|
-
const codecs = require('../codecs/node/index.js');
|
|
5
|
-
const { Color, default: framebuffer } = require('./framebuffer.js');
|
|
6
|
-
|
|
7
|
-
const wasm = {
|
|
8
|
-
svg: require('../wasm/node/svg.js'),
|
|
9
|
-
gif: require('../wasm/node/gif.js'),
|
|
10
|
-
jpeg: require('../wasm/node/jpeg.js'),
|
|
11
|
-
tiff: require('../wasm/node/tiff.js'),
|
|
12
|
-
// font: require('../wasm/node/font.js'),
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// todo: verbose range errors
|
|
16
|
-
|
|
17
|
-
const animation_formats = ['gif'];
|
|
18
|
-
const image_formats = ['png', 'jpeg', 'tiff'];
|
|
19
|
-
const all_formats = ['png', 'gif', 'jpeg', 'tiff'];
|
|
20
|
-
|
|
21
|
-
async function load(buffer) {
|
|
22
|
-
const u8 = mem.view(buffer);
|
|
23
|
-
if (0 === u8.length) throw new RangeError('empty buffer');
|
|
24
|
-
|
|
25
|
-
const meta = magic.buffer(u8);
|
|
26
|
-
if (!meta) throw new RangeError('unknown file format');
|
|
27
|
-
if ('image' !== meta.type) throw new RangeError('unsupported file type');
|
|
28
|
-
if (!all_formats.includes(meta.format)) throw new RangeError('unsupported image format');
|
|
29
|
-
|
|
30
|
-
if ('gif' === meta.format) return Animation.decode('gif', u8);
|
|
31
|
-
return Image.from('png' === meta.format ? png.decode(u8) : (await wasm[meta.format].init()).load(u8));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
class Frame {
|
|
35
|
-
timestamp = 0;
|
|
36
|
-
dispose = 'any';
|
|
37
|
-
|
|
38
|
-
constructor(width, height, buffer) {
|
|
39
|
-
this.image = buffer instanceof Image ? buffer : new Image(width | 0, height | 0, buffer);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
get width() { return this.image.width; }
|
|
43
|
-
get height() { return this.image.height; }
|
|
44
|
-
|
|
45
|
-
clone() {
|
|
46
|
-
const frame = new Frame(this.width, this.height, this.image.clone());
|
|
47
|
-
|
|
48
|
-
frame.dispose = this.dispose;
|
|
49
|
-
frame.timestamp = this.timestamp;
|
|
50
|
-
|
|
51
|
-
return frame;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static from(framebuffer) {
|
|
55
|
-
const frame = new Frame(framebuffer.width, framebuffer.height, framebuffer.u8 || framebuffer.buffer);
|
|
56
|
-
|
|
57
|
-
frame.dispose = framebuffer.dispose || 'any';
|
|
58
|
-
frame.timestamp = framebuffer.timestamp || 0;
|
|
59
|
-
|
|
60
|
-
return frame;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
class Image extends framebuffer {
|
|
65
|
-
constructor(width, height, buffer) {
|
|
66
|
-
super(width, height, buffer || new Uint8Array(4 * width * height));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
encode(format, options = {}) {
|
|
70
|
-
const method = options.sync ? 'encode' : 'encode_async';
|
|
71
|
-
if ('png' === format) return codecs.png[method](this.u8, { ...options, width: this.width, height: this.height });
|
|
72
|
-
if ('jpeg' === format) return codecs.jpeg[method](this.u8, { ...options, width: this.width, height: this.height });
|
|
73
|
-
if ('webp' === format) return codecs.webp[method](this.u8, { ...options, width: this.width, height: this.height });
|
|
74
|
-
|
|
75
|
-
throw new RangeError('invalid image format');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
static async decode(format, buffer, options = {}) {
|
|
79
|
-
buffer = mem.view('string' === typeof buffer ? Buffer.from(buffer) : buffer);
|
|
80
|
-
|
|
81
|
-
if ('auto' === format) {
|
|
82
|
-
const meta = magic.buffer(buffer);
|
|
83
|
-
if (!meta) throw new RangeError('unknown file format');
|
|
84
|
-
if ('image' !== meta.type) throw new RangeError('unsupported file type');
|
|
85
|
-
if (!image_formats.includes(meta.format)) throw new RangeError('unsupported image format');
|
|
86
|
-
|
|
87
|
-
format = meta.format;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
let frame;
|
|
91
|
-
if ('svg' === format) frame = (await wasm.svg.init()).load(buffer, options.fit ?? null);
|
|
92
|
-
|
|
93
|
-
else if (format in wasm) frame = (await wasm[format].init()).load(buffer);
|
|
94
|
-
|
|
95
|
-
if (!frame) throw new RangeError('invalid image format');
|
|
96
|
-
return new Image(frame.width, frame.height, frame.buffer);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
class Animation {
|
|
101
|
-
width = 0;
|
|
102
|
-
height = 0;
|
|
103
|
-
frames = [];
|
|
104
|
-
repeat = Infinity;
|
|
105
|
-
|
|
106
|
-
constructor(width, height) {
|
|
107
|
-
this.width = width | 0;
|
|
108
|
-
this.height = height | 0;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
clear() { this.frames.length = 0; }
|
|
112
|
-
[Symbol.iterator]() { return this.frames.values(); }
|
|
113
|
-
|
|
114
|
-
add(frame) {
|
|
115
|
-
if (this.width !== frame.width) throw new RangeError('invalid frame width');
|
|
116
|
-
else if (this.height !== frame.height) throw new RangeError('invalid frame height');
|
|
117
|
-
|
|
118
|
-
this.frames.push(frame);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
resize(type, width, height) {
|
|
122
|
-
this.width = width | 0;
|
|
123
|
-
this.height = height | 0;
|
|
124
|
-
for (const frame of this) frame.image.resize(type, this.width, this.height);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async encode(format, options = {}) {
|
|
128
|
-
if ('gif' === format) {
|
|
129
|
-
const quality = options.quality ?? 95;
|
|
130
|
-
const encoder = new codecs.gif.encoder(this.width, this.height);
|
|
131
|
-
|
|
132
|
-
let prev = { timestamp: 0 };
|
|
133
|
-
for (const frame of this.frames) {
|
|
134
|
-
encoder.add(frame.image.u8, {
|
|
135
|
-
x: 0,
|
|
136
|
-
y: 0,
|
|
137
|
-
quality,
|
|
138
|
-
width: frame.width,
|
|
139
|
-
speed: options.speed,
|
|
140
|
-
height: frame.height,
|
|
141
|
-
colors: options.colors,
|
|
142
|
-
dispose: frame.dispose,
|
|
143
|
-
delay: ((frame.timestamp - prev.timestamp) / 10) | 0,
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
prev = frame;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const repeat = options.repeat ?? this.repeat;
|
|
150
|
-
return encoder.finish({ ...options, repeat: Infinity === repeat ? null : repeat });
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
throw new RangeError('invalid animation format');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
static async decode(format, buffer, options = {}) {
|
|
157
|
-
buffer = mem.view(buffer);
|
|
158
|
-
|
|
159
|
-
if ('auto' === format) {
|
|
160
|
-
const meta = magic.buffer(buffer);
|
|
161
|
-
if (!meta) throw new RangeError('unknown file format');
|
|
162
|
-
if ('image' !== meta.type) throw new RangeError('unsupported file type');
|
|
163
|
-
if (!animation_formats.includes(meta.format)) throw new RangeError('unsupported animation format');
|
|
164
|
-
|
|
165
|
-
format = meta.format;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if ('gif' === format) {
|
|
169
|
-
const decoder = new (await wasm.gif.init()).Decoder(buffer);
|
|
170
|
-
|
|
171
|
-
const gwidth = decoder.width | 0;
|
|
172
|
-
const gheight = decoder.height | 0;
|
|
173
|
-
const animation = new Animation(gwidth, gheight);
|
|
174
|
-
const u32 = new Uint32Array(decoder.width * decoder.height);
|
|
175
|
-
const u8 = new Uint8Array(u32.buffer, u32.byteOffset, u32.byteLength);
|
|
176
|
-
|
|
177
|
-
let prev = { timestamp: 0 };
|
|
178
|
-
const frames = animation.frames;
|
|
179
|
-
for (const frame of decoder.frames()) {
|
|
180
|
-
let offset8 = 0 | 0;
|
|
181
|
-
let offset32 = 0 | 0;
|
|
182
|
-
const fx = frame.x | 0;
|
|
183
|
-
const fy = frame.y | 0;
|
|
184
|
-
const f8 = frame.buffer;
|
|
185
|
-
const mode = frame.dispose;
|
|
186
|
-
const width = frame.width | 0;
|
|
187
|
-
const height = frame.height | 0;
|
|
188
|
-
const f = frames[frames.push(new Frame(gwidth, gheight)) - 1];
|
|
189
|
-
const f32 = new Uint32Array(f8.buffer, f8.byteOffset, width * height);
|
|
190
|
-
|
|
191
|
-
const t8 = f.image.u8;
|
|
192
|
-
f.dispose = 'background';
|
|
193
|
-
const t32 = new Uint32Array(t8.buffer);
|
|
194
|
-
f.timestamp = prev.timestamp + 10 * frame.delay;
|
|
195
|
-
|
|
196
|
-
prev = f;
|
|
197
|
-
t8.set(u8);
|
|
198
|
-
|
|
199
|
-
if (2 === mode) {
|
|
200
|
-
for (let y = 0 | 0; y < height; y++) {
|
|
201
|
-
const y_offset = fx + gwidth * (y + fy) | 0;
|
|
202
|
-
|
|
203
|
-
for (let x = 0 | 0; x < width; x++) {
|
|
204
|
-
const x_offset = x + y_offset;
|
|
205
|
-
|
|
206
|
-
if (0 === f8[3 + offset8])
|
|
207
|
-
t32[x_offset] = u32[x_offset];
|
|
208
|
-
else t32[x_offset] = f32[offset32];
|
|
209
|
-
|
|
210
|
-
offset32++;
|
|
211
|
-
offset8 += 4;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
else if (3 === mode) {
|
|
217
|
-
for (let y = 0 | 0; y < height; y++) {
|
|
218
|
-
const y_offset = fx + gwidth * (y + fy) | 0;
|
|
219
|
-
|
|
220
|
-
for (let x = 0 | 0; x < width; x++) {
|
|
221
|
-
const x_offset = x + y_offset;
|
|
222
|
-
|
|
223
|
-
if (0 === f8[3 + offset8])
|
|
224
|
-
t32[x_offset] = u32[x_offset];
|
|
225
|
-
else t32[x_offset] = f32[offset32];
|
|
226
|
-
|
|
227
|
-
offset32++;
|
|
228
|
-
offset8 += 4;
|
|
229
|
-
u32[x_offset] = 0;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
else if (0 === mode || 1 === mode) {
|
|
235
|
-
t8.set(u8)
|
|
236
|
-
for (let y = 0 | 0; y < height; y++) {
|
|
237
|
-
const y_offset = fx + gwidth * (y + fy) | 0;
|
|
238
|
-
|
|
239
|
-
for (let x = 0 | 0; x < width; x++) {
|
|
240
|
-
const x_offset = x + y_offset;
|
|
241
|
-
|
|
242
|
-
if (0 === f8[3 + offset8])
|
|
243
|
-
t32[x_offset] = u32[x_offset];
|
|
244
|
-
else t32[x_offset] = f32[offset32];
|
|
245
|
-
|
|
246
|
-
offset32++;
|
|
247
|
-
offset8 += 4;
|
|
248
|
-
u32[x_offset] = t32[x_offset];
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return animation;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
throw new RangeError('invalid animation format');
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
1
|
+
const mem = require('./util/mem.js');
|
|
2
|
+
const png = require('../png/node.js');
|
|
3
|
+
const magic = require('./codecs/magic.js');
|
|
4
|
+
const codecs = require('../codecs/node/index.js');
|
|
5
|
+
const { Color, default: framebuffer } = require('./framebuffer.js');
|
|
6
|
+
|
|
7
|
+
const wasm = {
|
|
8
|
+
svg: require('../wasm/node/svg.js'),
|
|
9
|
+
gif: require('../wasm/node/gif.js'),
|
|
10
|
+
jpeg: require('../wasm/node/jpeg.js'),
|
|
11
|
+
tiff: require('../wasm/node/tiff.js'),
|
|
12
|
+
// font: require('../wasm/node/font.js'),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// todo: verbose range errors
|
|
16
|
+
|
|
17
|
+
const animation_formats = ['gif'];
|
|
18
|
+
const image_formats = ['png', 'jpeg', 'tiff'];
|
|
19
|
+
const all_formats = ['png', 'gif', 'jpeg', 'tiff'];
|
|
20
|
+
|
|
21
|
+
async function load(buffer) {
|
|
22
|
+
const u8 = mem.view(buffer);
|
|
23
|
+
if (0 === u8.length) throw new RangeError('empty buffer');
|
|
24
|
+
|
|
25
|
+
const meta = magic.buffer(u8);
|
|
26
|
+
if (!meta) throw new RangeError('unknown file format');
|
|
27
|
+
if ('image' !== meta.type) throw new RangeError('unsupported file type');
|
|
28
|
+
if (!all_formats.includes(meta.format)) throw new RangeError('unsupported image format');
|
|
29
|
+
|
|
30
|
+
if ('gif' === meta.format) return Animation.decode('gif', u8);
|
|
31
|
+
return Image.from('png' === meta.format ? png.decode(u8) : (await wasm[meta.format].init()).load(u8));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class Frame {
|
|
35
|
+
timestamp = 0;
|
|
36
|
+
dispose = 'any';
|
|
37
|
+
|
|
38
|
+
constructor(width, height, buffer) {
|
|
39
|
+
this.image = buffer instanceof Image ? buffer : new Image(width | 0, height | 0, buffer);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get width() { return this.image.width; }
|
|
43
|
+
get height() { return this.image.height; }
|
|
44
|
+
|
|
45
|
+
clone() {
|
|
46
|
+
const frame = new Frame(this.width, this.height, this.image.clone());
|
|
47
|
+
|
|
48
|
+
frame.dispose = this.dispose;
|
|
49
|
+
frame.timestamp = this.timestamp;
|
|
50
|
+
|
|
51
|
+
return frame;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static from(framebuffer) {
|
|
55
|
+
const frame = new Frame(framebuffer.width, framebuffer.height, framebuffer.u8 || framebuffer.buffer);
|
|
56
|
+
|
|
57
|
+
frame.dispose = framebuffer.dispose || 'any';
|
|
58
|
+
frame.timestamp = framebuffer.timestamp || 0;
|
|
59
|
+
|
|
60
|
+
return frame;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class Image extends framebuffer {
|
|
65
|
+
constructor(width, height, buffer) {
|
|
66
|
+
super(width, height, buffer || new Uint8Array(4 * width * height));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
encode(format, options = {}) {
|
|
70
|
+
const method = options.sync ? 'encode' : 'encode_async';
|
|
71
|
+
if ('png' === format) return codecs.png[method](this.u8, { ...options, width: this.width, height: this.height });
|
|
72
|
+
if ('jpeg' === format) return codecs.jpeg[method](this.u8, { ...options, width: this.width, height: this.height });
|
|
73
|
+
if ('webp' === format) return codecs.webp[method](this.u8, { ...options, width: this.width, height: this.height });
|
|
74
|
+
|
|
75
|
+
throw new RangeError('invalid image format');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static async decode(format, buffer, options = {}) {
|
|
79
|
+
buffer = mem.view('string' === typeof buffer ? Buffer.from(buffer) : buffer);
|
|
80
|
+
|
|
81
|
+
if ('auto' === format) {
|
|
82
|
+
const meta = magic.buffer(buffer);
|
|
83
|
+
if (!meta) throw new RangeError('unknown file format');
|
|
84
|
+
if ('image' !== meta.type) throw new RangeError('unsupported file type');
|
|
85
|
+
if (!image_formats.includes(meta.format)) throw new RangeError('unsupported image format');
|
|
86
|
+
|
|
87
|
+
format = meta.format;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let frame;
|
|
91
|
+
if ('svg' === format) frame = (await wasm.svg.init()).load(buffer, options.fit ?? null);
|
|
92
|
+
|
|
93
|
+
else if (format in wasm) frame = (await wasm[format].init()).load(buffer);
|
|
94
|
+
|
|
95
|
+
if (!frame) throw new RangeError('invalid image format');
|
|
96
|
+
return new Image(frame.width, frame.height, frame.buffer);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
class Animation {
|
|
101
|
+
width = 0;
|
|
102
|
+
height = 0;
|
|
103
|
+
frames = [];
|
|
104
|
+
repeat = Infinity;
|
|
105
|
+
|
|
106
|
+
constructor(width, height) {
|
|
107
|
+
this.width = width | 0;
|
|
108
|
+
this.height = height | 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
clear() { this.frames.length = 0; }
|
|
112
|
+
[Symbol.iterator]() { return this.frames.values(); }
|
|
113
|
+
|
|
114
|
+
add(frame) {
|
|
115
|
+
if (this.width !== frame.width) throw new RangeError('invalid frame width');
|
|
116
|
+
else if (this.height !== frame.height) throw new RangeError('invalid frame height');
|
|
117
|
+
|
|
118
|
+
this.frames.push(frame);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
resize(type, width, height) {
|
|
122
|
+
this.width = width | 0;
|
|
123
|
+
this.height = height | 0;
|
|
124
|
+
for (const frame of this) frame.image.resize(type, this.width, this.height);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async encode(format, options = {}) {
|
|
128
|
+
if ('gif' === format) {
|
|
129
|
+
const quality = options.quality ?? 95;
|
|
130
|
+
const encoder = new codecs.gif.encoder(this.width, this.height);
|
|
131
|
+
|
|
132
|
+
let prev = { timestamp: 0 };
|
|
133
|
+
for (const frame of this.frames) {
|
|
134
|
+
encoder.add(frame.image.u8, {
|
|
135
|
+
x: 0,
|
|
136
|
+
y: 0,
|
|
137
|
+
quality,
|
|
138
|
+
width: frame.width,
|
|
139
|
+
speed: options.speed,
|
|
140
|
+
height: frame.height,
|
|
141
|
+
colors: options.colors,
|
|
142
|
+
dispose: frame.dispose,
|
|
143
|
+
delay: ((frame.timestamp - prev.timestamp) / 10) | 0,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
prev = frame;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const repeat = options.repeat ?? this.repeat;
|
|
150
|
+
return encoder.finish({ ...options, repeat: Infinity === repeat ? null : repeat });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
throw new RangeError('invalid animation format');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static async decode(format, buffer, options = {}) {
|
|
157
|
+
buffer = mem.view(buffer);
|
|
158
|
+
|
|
159
|
+
if ('auto' === format) {
|
|
160
|
+
const meta = magic.buffer(buffer);
|
|
161
|
+
if (!meta) throw new RangeError('unknown file format');
|
|
162
|
+
if ('image' !== meta.type) throw new RangeError('unsupported file type');
|
|
163
|
+
if (!animation_formats.includes(meta.format)) throw new RangeError('unsupported animation format');
|
|
164
|
+
|
|
165
|
+
format = meta.format;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if ('gif' === format) {
|
|
169
|
+
const decoder = new (await wasm.gif.init()).Decoder(buffer);
|
|
170
|
+
|
|
171
|
+
const gwidth = decoder.width | 0;
|
|
172
|
+
const gheight = decoder.height | 0;
|
|
173
|
+
const animation = new Animation(gwidth, gheight);
|
|
174
|
+
const u32 = new Uint32Array(decoder.width * decoder.height);
|
|
175
|
+
const u8 = new Uint8Array(u32.buffer, u32.byteOffset, u32.byteLength);
|
|
176
|
+
|
|
177
|
+
let prev = { timestamp: 0 };
|
|
178
|
+
const frames = animation.frames;
|
|
179
|
+
for (const frame of decoder.frames()) {
|
|
180
|
+
let offset8 = 0 | 0;
|
|
181
|
+
let offset32 = 0 | 0;
|
|
182
|
+
const fx = frame.x | 0;
|
|
183
|
+
const fy = frame.y | 0;
|
|
184
|
+
const f8 = frame.buffer;
|
|
185
|
+
const mode = frame.dispose;
|
|
186
|
+
const width = frame.width | 0;
|
|
187
|
+
const height = frame.height | 0;
|
|
188
|
+
const f = frames[frames.push(new Frame(gwidth, gheight)) - 1];
|
|
189
|
+
const f32 = new Uint32Array(f8.buffer, f8.byteOffset, width * height);
|
|
190
|
+
|
|
191
|
+
const t8 = f.image.u8;
|
|
192
|
+
f.dispose = 'background';
|
|
193
|
+
const t32 = new Uint32Array(t8.buffer);
|
|
194
|
+
f.timestamp = prev.timestamp + 10 * frame.delay;
|
|
195
|
+
|
|
196
|
+
prev = f;
|
|
197
|
+
t8.set(u8);
|
|
198
|
+
|
|
199
|
+
if (2 === mode) {
|
|
200
|
+
for (let y = 0 | 0; y < height; y++) {
|
|
201
|
+
const y_offset = fx + gwidth * (y + fy) | 0;
|
|
202
|
+
|
|
203
|
+
for (let x = 0 | 0; x < width; x++) {
|
|
204
|
+
const x_offset = x + y_offset;
|
|
205
|
+
|
|
206
|
+
if (0 === f8[3 + offset8])
|
|
207
|
+
t32[x_offset] = u32[x_offset];
|
|
208
|
+
else t32[x_offset] = f32[offset32];
|
|
209
|
+
|
|
210
|
+
offset32++;
|
|
211
|
+
offset8 += 4;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
else if (3 === mode) {
|
|
217
|
+
for (let y = 0 | 0; y < height; y++) {
|
|
218
|
+
const y_offset = fx + gwidth * (y + fy) | 0;
|
|
219
|
+
|
|
220
|
+
for (let x = 0 | 0; x < width; x++) {
|
|
221
|
+
const x_offset = x + y_offset;
|
|
222
|
+
|
|
223
|
+
if (0 === f8[3 + offset8])
|
|
224
|
+
t32[x_offset] = u32[x_offset];
|
|
225
|
+
else t32[x_offset] = f32[offset32];
|
|
226
|
+
|
|
227
|
+
offset32++;
|
|
228
|
+
offset8 += 4;
|
|
229
|
+
u32[x_offset] = 0;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
else if (0 === mode || 1 === mode) {
|
|
235
|
+
t8.set(u8)
|
|
236
|
+
for (let y = 0 | 0; y < height; y++) {
|
|
237
|
+
const y_offset = fx + gwidth * (y + fy) | 0;
|
|
238
|
+
|
|
239
|
+
for (let x = 0 | 0; x < width; x++) {
|
|
240
|
+
const x_offset = x + y_offset;
|
|
241
|
+
|
|
242
|
+
if (0 === f8[3 + offset8])
|
|
243
|
+
t32[x_offset] = u32[x_offset];
|
|
244
|
+
else t32[x_offset] = f32[offset32];
|
|
245
|
+
|
|
246
|
+
offset32++;
|
|
247
|
+
offset8 += 4;
|
|
248
|
+
u32[x_offset] = t32[x_offset];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return animation;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
throw new RangeError('invalid animation format');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
261
|
module.exports = { load, Image, Frame, Color, Animation };
|