imagescript 1.2.8 → 1.2.15

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.
Files changed (91) hide show
  1. package/ImageScript.d.ts +223 -0
  2. package/ImageScript.js +60 -192
  3. package/LICENSE +619 -0
  4. package/README.md +17 -16
  5. package/bench/_.mjs +166 -0
  6. package/bench/all.mjs +4 -0
  7. package/bench/blur.mjs +59 -0
  8. package/bench/flip.mjs +63 -0
  9. package/bench/overlay.mjs +35 -0
  10. package/bench/package.json +7 -0
  11. package/bench/readme.md +64 -0
  12. package/bench/resize.mjs +52 -0
  13. package/codecs/LICENSE +21 -0
  14. package/codecs/lib.d.ts +50 -0
  15. package/codecs/lib.js +6 -0
  16. package/codecs/lib.mjs +1 -0
  17. package/codecs/node/bin/arm64-darwin.node +0 -0
  18. package/{node → codecs/node/bin}/arm64-linux.node +0 -0
  19. package/codecs/node/bin/x64-darwin.node +0 -0
  20. package/{node → codecs/node/bin}/x64-linux.node +0 -0
  21. package/{node → codecs/node/bin}/x64-win32.node +0 -0
  22. package/codecs/node/index.js +3 -0
  23. package/codecs/node/index.mjs +6 -0
  24. package/codecs/package.json +14 -0
  25. package/codecs/readme.md +38 -0
  26. package/{wasm/simd.wasm → codecs/tests/gif.js} +0 -0
  27. package/{wasm/unknown.wasm → codecs/tests/jpeg.js} +0 -0
  28. package/codecs/tests/png.js +0 -0
  29. package/codecs/tests/results/gif/1024.nis.gif +0 -0
  30. package/codecs/tests/results/gif/1024.skyra.gif +0 -0
  31. package/codecs/tests/results/gif/256.nis.gif +0 -0
  32. package/codecs/tests/results/gif/256.skyra.gif +0 -0
  33. package/codecs/tests/results/jpeg/256.jpegjs.jpeg +0 -0
  34. package/codecs/tests/results/jpeg/256.nis.jpeg +0 -0
  35. package/codecs/tests/results/jpeg/256.sharp.jpeg +0 -0
  36. package/codecs/tests/results/jpeg/512.jpegjs.jpeg +0 -0
  37. package/codecs/tests/results/jpeg/512.nis.jpeg +0 -0
  38. package/codecs/tests/results/jpeg/512.sharp.jpeg +0 -0
  39. package/codecs/tests/results/jpeg/64.jpegjs.jpeg +0 -0
  40. package/codecs/tests/results/jpeg/64.nis.jpeg +0 -0
  41. package/codecs/tests/results/jpeg/64.sharp.jpeg +0 -0
  42. package/codecs/tests/results/png/256.jis.png +0 -0
  43. package/codecs/tests/results/png/256.nis.png +0 -0
  44. package/codecs/tests/results/png/256.sharp.png +0 -0
  45. package/codecs/tests/results/png/256.upng.png +0 -0
  46. package/codecs/tests/results/png/512.jis.png +0 -0
  47. package/codecs/tests/results/png/512.nis.png +0 -0
  48. package/codecs/tests/results/png/512.sharp.png +0 -0
  49. package/codecs/tests/results/png/512.upng.png +0 -0
  50. package/codecs/tests/results/png/64.jis.png +0 -0
  51. package/codecs/tests/results/png/64.nis.png +0 -0
  52. package/codecs/tests/results/png/64.sharp.png +0 -0
  53. package/codecs/tests/results/png/64.upng.png +0 -0
  54. package/codecs/tests/webp.js +0 -0
  55. package/codecs/wasm/bin/codecs.wasm +0 -0
  56. package/codecs/wasm/bin/simd.wasm +0 -0
  57. package/codecs/wasm/bundle/browser.js +1 -0
  58. package/codecs/wasm/index.js +1 -0
  59. package/codecs/wasm/index.mjs +1 -0
  60. package/package.json +17 -4
  61. package/png/README.md +1 -0
  62. package/png/src/{crc.js → crc.mjs} +0 -0
  63. package/png/src/{mem.js → mem.mjs} +0 -0
  64. package/png/src/{png.js → png.mjs} +3 -3
  65. package/png/src/{zlib.js → zlib.mjs} +0 -0
  66. package/tsconfig.json +21 -0
  67. package/v2/framebuffer.js +94 -115
  68. package/v2/framebuffer.mjs +9 -10
  69. package/v2/framebuffer.rs +1264 -0
  70. package/v2/index.js +1 -1
  71. package/v2/ops/{blur.js → blur.mjs} +0 -0
  72. package/v2/ops/{color.js → color.mjs} +0 -0
  73. package/v2/ops/{crop.js → crop.mjs} +0 -0
  74. package/v2/ops/{fill.js → fill.mjs} +0 -0
  75. package/v2/ops/{flip.js → flip.mjs} +0 -0
  76. package/v2/ops/index.mjs +9 -0
  77. package/v2/ops/{iterator.js → iterator.mjs} +6 -6
  78. package/v2/ops/overlay.mjs +63 -0
  79. package/v2/ops/{resize.js → resize.mjs} +3 -3
  80. package/v2/ops/{rotate.js → rotate.mjs} +8 -9
  81. package/v2/util/{mem.js → mem.mjs} +3 -5
  82. package/wasm/any/font.wasm +0 -0
  83. package/wasm/any/png.wasm +0 -0
  84. package/wasm/node/png.js +49 -0
  85. package/node/arm64-darwin.node +0 -0
  86. package/node/index.js +0 -7
  87. package/node/x64-darwin.node +0 -0
  88. package/v2/ops/index.js +0 -9
  89. package/v2/ops/overlay.js +0 -55
  90. package/wasm/node.js +0 -23
  91. package/wasm/web.js +0 -10
@@ -0,0 +1,223 @@
1
+ export class Image {
2
+ private __width__: number;
3
+ private __height__: number;
4
+ private __buffer__: ArrayBuffer;
5
+ private __view__: DataView;
6
+ private __u32__: Uint32Array;
7
+ bitmap: Uint8ClampedArray;
8
+
9
+ constructor(width: number, height: number);
10
+
11
+ private toString(): string;
12
+
13
+ get width(): number;
14
+
15
+ get height(): number;
16
+
17
+ * [Symbol.iterator](): void;
18
+
19
+ * iterateWithColors(): Generator<[x: number, y: number, color: number], void, unknown>;
20
+
21
+ static rgbaToColor(r: number, g: number, b: number, a: number): number;
22
+
23
+ static rgbToColor(r: number, g: number, b: number): number;
24
+
25
+ static hslaToColor(h: number, s: number, l: number, a: number): number;
26
+
27
+ static hslToColor(h: number, s: number, l: number): number;
28
+
29
+ static rgbaToHSLA(r: number, g: number, b: number, a: number): number[];
30
+
31
+ static colorToRGBA(color: number): number[];
32
+
33
+ static colorToRGB(color: number): number[];
34
+
35
+ getPixelAt(x: number, y: number): number;
36
+
37
+ getRGBAAt(x: number, y: number): Uint8ClampedArray;
38
+
39
+ setPixelAt(x: number, y: number, pixelColor: number): Image;
40
+
41
+ private __set_pixel__(x: number, y: number, pixelColor: number): void;
42
+
43
+ private __check_boundaries__(x: number, y: number): void;
44
+
45
+ private static get __out_of_bounds__(): string;
46
+
47
+ fill(color: number | colorFunction): Image;
48
+
49
+ clone(): Image;
50
+
51
+ static get RESIZE_NEAREST_NEIGHBOR(): string;
52
+
53
+ static get RESIZE_AUTO(): number;
54
+
55
+ scale(factor: number, mode?: string): Image;
56
+
57
+ private __scale__(factor: number, mode?: string);
58
+
59
+ resize(width: number, height: number, mode?: string): Image;
60
+
61
+ contain(width: number, height: number, mode?: string): Image;
62
+
63
+ fit(width: number, height: number, mode?: string): Image;
64
+
65
+ cover(width: number, height: number, mode?: string): Image;
66
+
67
+ private __resize__(width: number, height: number, mode?: string): Image;
68
+
69
+ private __resize_nearest_neighbor__(width: number, height: number): Image;
70
+
71
+ crop(x: number, y: number, width: number, height: number): Image;
72
+
73
+ private __crop__(x: number, y: number, width: number, height: number): Image;
74
+
75
+ drawBox(x: number, y: number, width: number, height: number, color: number | colorFunction): Image;
76
+
77
+ private __fast_box__(x: number, y: number, width: number, height: number, color: number): Image;
78
+
79
+ drawCircle(x: number, y: number, radius: number, color: number | colorFunction): Image;
80
+
81
+ cropCircle(max?: boolean, feathering?: number): Image;
82
+
83
+ opacity(opacity: number, absolute?: boolean): Image;
84
+
85
+ red(saturation: number, absolute?: boolean): Image;
86
+
87
+ green(saturation: number, absolute?: boolean): Image;
88
+
89
+ blue(saturation: number, absolute?: boolean): Image;
90
+
91
+ private __set_channel_value__(value: number, absolute: boolean, offset: number): void;
92
+
93
+ lightness(value: number, absolute?: boolean): Image;
94
+
95
+ saturation(value: number, absolute?: boolean): Image;
96
+
97
+ composite(source: Image, x?: number, y?: number): Image;
98
+
99
+ invert(): Image;
100
+
101
+ invertValue(): Image;
102
+
103
+ invertSaturation(): Image;
104
+
105
+ invertHue(): Image;
106
+
107
+ hueShift(degrees: number): Image;
108
+
109
+ averageColor(): number;
110
+
111
+ dominantColor(ignoreBlack?: boolean, ignoreWhite?: boolean, bwThreshold?: number): number;
112
+
113
+ rotate(angle: number, resize?: boolean): Image;
114
+
115
+ private __apply__(image: Image | Frame): Image | Frame;
116
+
117
+ static gradient(colors: { [position: number]: number; }): ((position: number) => number);
118
+
119
+ roundCorners(radius?: number): Image;
120
+
121
+ private static __gradient__(startColor: number, endColor: number): number;
122
+
123
+ fisheye(radius?: number): Image;
124
+
125
+ async encode(compression?: number, metadata?: PNGMetadata): Promise<Uint8Array>;
126
+ async encode(metadata?: PNGMetadata): Promise<Uint8Array>;
127
+
128
+ async encodeJPEG(quality?: number): Promise<Uint8Array>;
129
+
130
+ async encodeWEBP(quality?: null | number): Promise<Uint8Array>;
131
+
132
+ static async encode(data: Buffer | Uint8Array): Promise<Image>
133
+
134
+ static get SVG_MODE_SCALE(): number;
135
+
136
+ static get SVG_MODE_WIDTH(): number;
137
+
138
+ static get SVG_MODE_HEIGHT(): number;
139
+
140
+ static async renderSVG(svg: string, size?: number, mode?: number): Promise<Image>;
141
+
142
+ static async renderText(font: Uint8Array, scale: number, text: string, color?: number, layout?: TextLayout): Promise<Image>;
143
+ };
144
+
145
+ export class Frame extends Image {
146
+ static get DISPOSAL_KEEP(): string;
147
+
148
+ static get DISPOSAL_PREVIOUS(): string;
149
+
150
+ static get DISPOSAL_BACKGROUND(): string
151
+
152
+ private static __convert_disposal_mode__(mode: string | number): any;
153
+
154
+ constructor(width: number, height: number, duration?: number, xOffset?: number, yOffset?: number, disposalMode?: typeof Frame.DISPOSAL_KEEP | string);
155
+
156
+ toString(): string;
157
+
158
+ static from(image: Image, duration?: number, xOffset?: number, yOffset?: number, disposalMode?: typeof Frame.DISPOSAL_KEEP | string): Frame;
159
+
160
+ resize(width: number, height: number, mode?: typeof Image.RESIZE_NEAREST_NEIGHBOR | string): Image;
161
+ };
162
+
163
+ export class GIF extends Array {
164
+ constructor(frames: Frame[], loopCount?: number);
165
+
166
+ get width(): number;
167
+
168
+ get height(): number;
169
+
170
+ toString(): string;
171
+
172
+ * [Symbol.iterator](): Generator<Frame, void, *>
173
+
174
+ slice(start: number, end: number): GIF;
175
+
176
+ get duration(): number;
177
+
178
+ async encode(quality?: number): Promise<Uint8Array>
179
+
180
+ static async decode(data: Buffer | Uint8Array, onlyExtractFirstFrame?: boolean): Promise<GIF>;
181
+
182
+ resize(width: number, height: number, mode?: typeof Image.RESIZE_NEAREST_NEIGHBOR | string): void;
183
+ }
184
+
185
+ export class TextLayout {
186
+ constructor(options?: {
187
+ maxWidth?: number,
188
+ maxHeight?: number,
189
+ wrapStyle?: string,
190
+ verticalAlign?: string,
191
+ horizontalAlign?: string,
192
+ wrapHardBreaks?: boolean
193
+ });
194
+ };
195
+
196
+ export class ImageType {
197
+ static getType(data: Buffer | Uint8Array): string | null;
198
+
199
+ static isPNG(view: DataView): boolean;
200
+
201
+ static isJPEG(view: DataView): boolean;
202
+
203
+ static isTIFF(view: DataView): boolean;
204
+
205
+ static isGIF(view: DataView): boolean;
206
+ };
207
+
208
+ export function decode(data: Uint8Array | Buffer, onlyExtractFirstFrame?: boolean): Promise<GIF | Image>;
209
+
210
+ type colorFunction = (x: number, y: number) => number;
211
+
212
+ type PNGMetadata = {
213
+ title?: string,
214
+ author?: string,
215
+ description?: string,
216
+ copyright?: string,
217
+ creationTime?: string | number | Date,
218
+ software?: string,
219
+ disclaimer?: string,
220
+ warning?: string,
221
+ source?: string,
222
+ comment?: string
223
+ };
package/ImageScript.js CHANGED
@@ -1,11 +1,13 @@
1
1
  const png = require('./png/node.js');
2
2
  const mem = require('./utils/mem.js');
3
- const codecs = require('./node/index.js');
4
3
  const {version} = require('./package.json');
4
+ const codecs = require('./codecs/node/index.js');
5
+ const { default: v2 } = require('./v2/framebuffer.js');
5
6
 
6
7
  // old
7
8
  const svglib = require('./wasm/node/svg.js');
8
9
  const giflib = require('./wasm/node/gif.js');
10
+ const pnglib = require('./wasm/node/png.js');
9
11
  const fontlib = require('./wasm/node/font.js');
10
12
  const jpeglib = require('./wasm/node/jpeg.js');
11
13
  const tifflib = require('./wasm/node/tiff.js');
@@ -82,26 +84,16 @@ class Image {
82
84
  * @yields {number[]} The coordinates of the pixel ([x, y])
83
85
  * @returns {void}
84
86
  */
85
- * [Symbol.iterator]() {
86
- for (let y = 1; y <= this.height; y++) {
87
- for (let x = 1; x <= this.width; x++) {
88
- yield [x, y];
89
- }
90
- }
87
+ *[Symbol.iterator]() {
88
+ for (const x of new v2(this.width, this.height, this.bitmap)[Symbol.iterator]()) yield (x[0]++, x[1]++, x)
91
89
  }
92
90
 
93
91
  /**
94
92
  * Yields an [x, y, color] array for every pixel in the image
95
93
  * @yields {number[]} The coordinates and color of the pixel ([x, y, color])
96
94
  */
97
- * iterateWithColors() {
98
- let offset = 0;
99
- for (let y = 1; y <= this.height; y++) {
100
- for (let x = 1; x <= this.width; x++) {
101
- yield [x, y, this.__view__.getUint32(offset, false)];
102
- offset += 4;
103
- }
104
- }
95
+ *iterateWithColors() {
96
+ for (const x of new v2(this.width, this.height, this.bitmap).pixels('int')) yield (x[0]++, x[1]++, x);
105
97
  }
106
98
 
107
99
  /**
@@ -319,19 +311,7 @@ class Image {
319
311
  * @returns {Image}
320
312
  */
321
313
  fill(color) {
322
- const type = typeof color;
323
- if (type !== 'function') {
324
- this.__view__.setUint32(0, color, false);
325
- this.__u32__.fill(this.__u32__[0]);
326
- } else {
327
- let offset = 0;
328
- for (let y = 1; y <= this.height; y++) {
329
- for (let x = 1; x <= this.width; x++) {
330
- this.__view__.setUint32(offset, color(x, y), false);
331
- offset += 4;
332
- }
333
- }
334
- }
314
+ new v2(this.width, this.height, this.bitmap).fill(color);
335
315
 
336
316
  return this;
337
317
  }
@@ -392,6 +372,48 @@ class Image {
392
372
  return this.__apply__(image);
393
373
  }
394
374
 
375
+ /**
376
+ * Resizes the image so it is contained in the given bounding box.
377
+ * Can return an image with one axis smaller than the given bounding box.
378
+ * @param {number} width The width of the bounding box
379
+ * @param {number} height The height of the bounding box
380
+ * @param {string} [mode=Image.RESIZE_NEAREST_NEIGHBOR] The resizing mode to use
381
+ * @returns {Image} The resized image
382
+ */
383
+ contain(width, height, mode = Image.RESIZE_NEAREST_NEIGHBOR) {
384
+ const scaleFactor = width / height > this.width / this.height ? height / this.height : width / this.width;
385
+ return this.scale(scaleFactor, mode);
386
+ }
387
+
388
+ /**
389
+ * Resizes the image so it is contained in the given bounding box, placing it in the center of the given bounding box.
390
+ * Always returns the exact dimensions of the bounding box.
391
+ * @param {number} width The width of the bounding box
392
+ * @param {number} height The height of the bounding box
393
+ * @param {string} [mode=Image.RESIZE_NEAREST_NEIGHBOR] The resizing mode to use
394
+ * @returns {Image} The resized image
395
+ */
396
+ fit(width, height, mode = Image.RESIZE_NEAREST_NEIGHBOR) {
397
+ const result = new Image(width, height);
398
+ this.contain(width, height, mode);
399
+ result.composite(this, (width - this.width) / 2, (height - this.height) / 2);
400
+ return this.__apply__(result);
401
+ }
402
+
403
+ /**
404
+ * Resizes the image so it covers the given bounding box, cropping the overflowing edges.
405
+ * Always returns the exact dimensions of the bounding box.
406
+ * @param {number} width The width of the bounding box
407
+ * @param {number} height The height of the bounding box
408
+ * @param {string} [mode=Image.RESIZE_NEAREST_NEIGHBOR] The resizing mode to use
409
+ * @returns {Image} The resized image
410
+ */
411
+ cover(width, height, mode = Image.RESIZE_NEAREST_NEIGHBOR) {
412
+ const scaleFactor = width / height > this.width / this.height ? width / this.width : height / this.height;
413
+ const result = this.scale(scaleFactor, mode);
414
+ return result.crop((result.width - width) / 2, (result.height - height) / 2, width, height);
415
+ }
416
+
395
417
  /** @private */
396
418
  __resize__(width, height, mode = Image.RESIZE_NEAREST_NEIGHBOR) {
397
419
  if (width === Image.RESIZE_AUTO && height === Image.RESIZE_AUTO) throw new Error('RESIZE_AUTO can only be used for either width or height, not for both');
@@ -420,18 +442,9 @@ class Image {
420
442
  */
421
443
  __resize_nearest_neighbor__(width, height) {
422
444
  const image = new this.constructor(width, height);
445
+ const frame = new v2(this.width, this.height, this.bitmap).resize('nearest', width, height);
423
446
 
424
- for (let y = 0; y < height; y++) {
425
- for (let x = 0; x < width; x++) {
426
- const ySrc = Math.floor((y * this.height) / height);
427
- const xSrc = Math.floor((x * this.width) / width);
428
-
429
- const destPos = (y * width + x) * 4;
430
- const srcPos = (ySrc * this.width + xSrc) * 4;
431
-
432
- image.__view__.setUint32(destPos, this.__view__.getUint32(srcPos, false), false);
433
- }
434
- }
447
+ image.bitmap.set(frame.u8);
435
448
 
436
449
  return image;
437
450
  }
@@ -565,19 +578,7 @@ class Image {
565
578
  * @returns {Image}
566
579
  */
567
580
  cropCircle(max = false, feathering = 0) {
568
- const rad = Math[max ? 'max' : 'min'](this.width, this.height) / 2;
569
- const radSquared = rad ** 2;
570
- const centerX = this.width / 2;
571
- const centerY = this.height / 2;
572
-
573
- for (const [x, y] of this) {
574
- const distanceFromCenter = (x - centerX) ** 2 + (y - centerY) ** 2;
575
- const alphaIdx = ((y - 1) * this.width + (x - 1)) * 4 + 3;
576
- if (distanceFromCenter > radSquared)
577
- this.bitmap[alphaIdx] = 0;
578
- else if (feathering)
579
- this.bitmap[alphaIdx] *= Math.max(0, Math.min(1, 1 - (distanceFromCenter / radSquared) * feathering ** (1 / 2)));
580
- }
581
+ new v2(this.width, this.height, this.bitmap).crop('circle', feathering);
581
582
 
582
583
  return this;
583
584
  }
@@ -693,48 +694,11 @@ class Image {
693
694
  * @returns {Image}
694
695
  */
695
696
  composite(source, x = 0, y = 0) {
696
- x = ~~x;
697
- y = ~~y;
698
-
699
- for (let yy = 0; yy < source.height; yy++) {
700
- let y_offset = y + yy;
701
- if (y_offset < 0) continue;
702
- if (y_offset >= this.height) break;
703
-
704
- for (let xx = 0; xx < source.width; xx++) {
705
- let x_offset = x + xx;
706
- if (x_offset < 0) continue;
707
- if (x_offset >= this.width) break;
708
-
709
- const offset = 4 * (x_offset + y_offset * this.width);
710
- const fg = source.__view__.getUint32(4 * (xx + yy * source.width), false);
711
- const bg = this.__view__.getUint32(offset, false);
712
-
713
- if ((fg & 0xff) === 0xff) this.__view__.setUint32(offset, fg, false);
714
- else if ((fg & 0xff) === 0x00) this.__view__.setUint32(offset, bg, false);
715
- else this.__view__.setUint32(offset, Image.__alpha_blend__(fg, bg), false);
716
- }
717
- }
697
+ new v2(this.width, this.height, this.bitmap).overlay(new v2(source.width, source.height, source.bitmap), x, y);
718
698
 
719
699
  return this;
720
700
  }
721
701
 
722
- /**
723
- * @private
724
- * @param {number} fg
725
- * @param {number} bg
726
- * @returns {number}
727
- */
728
- static __alpha_blend__(fg, bg) {
729
- const fa = fg & 0xff;
730
- const alpha = fa + 1;
731
- const inv_alpha = 256 - fa;
732
- const r = (alpha * (fg >>> 24) + inv_alpha * (bg >>> 24)) >> 8;
733
- const b = (alpha * (fg >> 8 & 0xff) + inv_alpha * (bg >> 8 & 0xff)) >> 8;
734
- const g = (alpha * (fg >> 16 & 0xff) + inv_alpha * (bg >> 16 & 0xff)) >> 8;
735
- return (((r & 0xff) << 24) | ((g & 0xff) << 16) | ((b & 0xff) << 8) | (Math.max(fa, bg & 0xff) & 0xff));
736
- }
737
-
738
702
  /**
739
703
  * Inverts the images colors
740
704
  * @returns {Image}
@@ -857,110 +821,14 @@ class Image {
857
821
  * @param {boolean} resize Whether to resize the image so it fits all pixels or just ignore outlying pixels
858
822
  */
859
823
  rotate(angle, resize = true) {
860
- if (angle % 360 === 0) return this;
861
- if (angle % 180 === 0) return this.__rotate_180__();
862
-
863
- const rad = Math.PI * (angle / 180);
864
-
865
- const sin = Math.sin(rad);
866
- const cos = Math.cos(rad);
867
-
868
- const width = resize
869
- ? Math.abs(this.width * sin) + Math.abs(this.height * cos)
870
- : this.width;
871
- const height = resize
872
- ? Math.abs(this.width * cos) + Math.abs(this.height * sin)
873
- : this.height;
874
-
875
- const out = new Image(width, height);
824
+ const frame = new v2(this.width, this.height, this.bitmap).rotate(360 - (angle % 360), resize);
876
825
 
877
- const out_cx = width / 2 - .5;
878
- const out_cy = height / 2 - .5;
879
- const src_cx = this.width / 2 - .5;
880
- const src_cy = this.height / 2 - .5;
881
-
882
- let h = 0;
883
- do {
884
- let w = 0;
885
- const ysin = src_cx - sin * (h - out_cy);
886
- const ycos = src_cy + cos * (h - out_cy);
887
-
888
- do {
889
- const xf = ysin + cos * (w - out_cx);
890
- const yf = ycos + sin * (w - out_cx);
891
- Image.__interpolate__(this, out, w, h, xf, yf);
892
- } while (w++ < width);
893
- } while (h++ < height);
826
+ const out = new Image(frame.width, frame.height);
894
827
 
828
+ out.bitmap.set(frame.u8);
895
829
  return this.__apply__(out);
896
830
  }
897
831
 
898
- /**
899
- * @returns {Image}
900
- * @private
901
- */
902
- __rotate_180__() {
903
- let offset = 0;
904
- this.bitmap.reverse();
905
- while (offset < this.bitmap.length) this.bitmap.subarray(offset, offset += 4).reverse();
906
-
907
- return this;
908
- }
909
-
910
- /**
911
- * @param {Image} src
912
- * @param {Image} out
913
- * @param {number} x0
914
- * @param {number} y0
915
- * @param {number} x1
916
- * @param {number} y1
917
- * @private
918
- */
919
- static __interpolate__(src, out, x0, y0, x1, y1) {
920
- const x2 = ~~x1;
921
- const y2 = ~~y1;
922
- const xq = x1 - x2;
923
- const yq = y1 - y2;
924
- const out_slice = out.bitmap.subarray(4 * (x0 + y0 * out.width), -4);
925
-
926
- const ref = {
927
- r: 0,
928
- g: 0,
929
- b: 0,
930
- a: 0,
931
- };
932
-
933
- Image.__pawn__(x2, y2, (1 - xq) * (1 - yq), ref, src);
934
- Image.__pawn__(1 + x2, y2, xq * (1 - yq), ref, src);
935
- Image.__pawn__(x2, 1 + y2, (1 - xq) * yq, ref, src);
936
- Image.__pawn__(1 + x2, 1 + y2, xq * yq, ref, src);
937
-
938
- out_slice[3] = ref.a;
939
- out_slice[0] = ref.r / ref.a;
940
- out_slice[1] = ref.g / ref.a;
941
- out_slice[2] = ref.b / ref.a;
942
- }
943
-
944
- /** @private */
945
- static __pawn__(point0, point1, weight, ref, src) {
946
- if (
947
- point0 > 0
948
- && point1 > 0
949
- && point0 < src.width
950
- && point1 < src.height
951
- ) {
952
- const offset = 4 * (point0 + point1 * src.width);
953
- const src_slice = src.bitmap.subarray(offset, offset + 4);
954
-
955
- const wa = weight * src_slice[3];
956
-
957
- ref.a += wa;
958
- ref.r += wa * src_slice[0];
959
- ref.g += wa * src_slice[1];
960
- ref.b += wa * src_slice[2];
961
- }
962
- }
963
-
964
832
  /**
965
833
  * @private
966
834
  * @param {Image|Frame} image
@@ -1215,9 +1083,9 @@ class Image {
1215
1083
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1216
1084
 
1217
1085
  if (ImageType.isPNG(view)) { // PNG
1218
- const {width, height, pixels} = await png.decode(data);
1086
+ const {width, height, framebuffer} = (await pnglib.init()).decode(data);
1219
1087
  image = new Image(width, height);
1220
- image.bitmap.set(pixels);
1088
+ image.bitmap.set(framebuffer);
1221
1089
  } else if (ImageType.isJPEG(view)) { // JPEG
1222
1090
  const framebuffer = (await jpeglib.init()).decode(data);
1223
1091
 
@@ -1631,7 +1499,7 @@ class GIF extends Array {
1631
1499
  }
1632
1500
 
1633
1501
  else if (0 === mode || 1 === mode) {
1634
- t8.set(u8)
1502
+ t8.set(u8);
1635
1503
  for (let y = 0 | 0; y < height; y++) {
1636
1504
  const y_offset = fx + gwidth * (y + fy) | 0;
1637
1505