imagescript 1.3.1 → 3.1.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.
Files changed (132) hide show
  1. package/LICENSE +4 -1
  2. package/README.md +76 -26
  3. package/THIRD_PARTY_NOTICES.md +156 -0
  4. package/dist/animation.d.ts +55 -0
  5. package/dist/animation.js +167 -0
  6. package/dist/codecs/crc.d.ts +9 -0
  7. package/dist/codecs/crc.js +29 -0
  8. package/dist/codecs/exif.d.ts +11 -0
  9. package/dist/codecs/exif.js +27 -0
  10. package/dist/codecs/gif.d.ts +44 -0
  11. package/dist/codecs/gif.js +401 -0
  12. package/dist/codecs/jpeg.d.ts +35 -0
  13. package/dist/codecs/jpeg.js +980 -0
  14. package/dist/codecs/png.d.ts +97 -0
  15. package/dist/codecs/png.js +664 -0
  16. package/dist/codecs/quantize.d.ts +25 -0
  17. package/dist/codecs/quantize.js +302 -0
  18. package/dist/codecs/tga.d.ts +16 -0
  19. package/dist/codecs/tga.js +203 -0
  20. package/dist/codecs/tiff.d.ts +6 -0
  21. package/dist/codecs/tiff.js +546 -0
  22. package/dist/codecs/vp8-tables.d.ts +10 -0
  23. package/dist/codecs/vp8-tables.js +17 -0
  24. package/dist/codecs/vp8.d.ts +8 -0
  25. package/dist/codecs/vp8.js +711 -0
  26. package/dist/codecs/vp8l.d.ts +62 -0
  27. package/dist/codecs/vp8l.js +909 -0
  28. package/dist/codecs/webp.d.ts +67 -0
  29. package/dist/codecs/webp.js +280 -0
  30. package/dist/codecs/zlib.d.ts +22 -0
  31. package/dist/codecs/zlib.js +607 -0
  32. package/dist/frame.d.ts +57 -0
  33. package/dist/frame.js +65 -0
  34. package/dist/gif.d.ts +31 -0
  35. package/dist/gif.js +20 -0
  36. package/dist/image-type.d.ts +24 -0
  37. package/dist/image-type.js +64 -0
  38. package/dist/image.d.ts +553 -0
  39. package/dist/image.js +1156 -0
  40. package/dist/index.d.ts +23 -0
  41. package/dist/index.js +20 -0
  42. package/dist/package.json +1 -0
  43. package/dist/render/font.d.ts +92 -0
  44. package/dist/render/font.js +954 -0
  45. package/dist/render/hinting.d.ts +110 -0
  46. package/dist/render/hinting.js +1236 -0
  47. package/dist/render/svg.d.ts +11 -0
  48. package/dist/render/svg.js +1717 -0
  49. package/dist/render/text.d.ts +63 -0
  50. package/dist/render/text.js +342 -0
  51. package/dist/text-layout.d.ts +46 -0
  52. package/dist/text-layout.js +35 -0
  53. package/dist/util/blur.d.ts +13 -0
  54. package/dist/util/blur.js +146 -0
  55. package/dist/util/mem.d.ts +10 -0
  56. package/dist/util/mem.js +23 -0
  57. package/dist/util/resample.d.ts +12 -0
  58. package/dist/util/resample.js +99 -0
  59. package/dist/util/utf8.d.ts +5 -0
  60. package/dist/util/utf8.js +55 -0
  61. package/dist/version.d.ts +1 -0
  62. package/dist/version.js +1 -0
  63. package/licenses/Apache-2.0.txt +202 -0
  64. package/licenses/FTL.txt +169 -0
  65. package/licenses/README.ijg +260 -0
  66. package/package.json +31 -7
  67. package/ImageScript.d.ts +0 -954
  68. package/ImageScript.js +0 -1669
  69. package/bench/_.mjs +0 -166
  70. package/bench/all.mjs +0 -4
  71. package/bench/blur.mjs +0 -59
  72. package/bench/flip.mjs +0 -63
  73. package/bench/overlay.mjs +0 -35
  74. package/bench/package.json +0 -7
  75. package/bench/readme.md +0 -64
  76. package/bench/resize.mjs +0 -52
  77. package/codecs/LICENSE +0 -21
  78. package/codecs/lib.d.ts +0 -50
  79. package/codecs/lib.js +0 -6
  80. package/codecs/lib.mjs +0 -1
  81. package/codecs/node/bin/arm64-darwin.node +0 -0
  82. package/codecs/node/bin/arm64-linux.node +0 -0
  83. package/codecs/node/bin/x64-darwin.node +0 -0
  84. package/codecs/node/bin/x64-linux.node +0 -0
  85. package/codecs/node/bin/x64-win32.node +0 -0
  86. package/codecs/node/index.js +0 -3
  87. package/codecs/node/index.mjs +0 -6
  88. package/codecs/package.json +0 -14
  89. package/codecs/readme.md +0 -38
  90. package/codecs/wasm/bin/codecs.wasm +0 -0
  91. package/codecs/wasm/bin/simd.wasm +0 -0
  92. package/codecs/wasm/bundle/browser.js +0 -1
  93. package/codecs/wasm/index.js +0 -1
  94. package/codecs/wasm/index.mjs +0 -1
  95. package/png/README.md +0 -1
  96. package/png/node.js +0 -1137
  97. package/png/src/crc.mjs +0 -49
  98. package/png/src/mem.mjs +0 -24
  99. package/png/src/png.mjs +0 -254
  100. package/png/src/zlib.mjs +0 -618
  101. package/tsconfig.json +0 -21
  102. package/utils/mem.js +0 -26
  103. package/v2/codecs/magic.js +0 -25
  104. package/v2/framebuffer.d.ts +0 -67
  105. package/v2/framebuffer.js +0 -2352
  106. package/v2/framebuffer.mjs +0 -126
  107. package/v2/framebuffer.rs +0 -1264
  108. package/v2/index.d.ts +0 -84
  109. package/v2/index.js +0 -261
  110. package/v2/ops/blur.mjs +0 -303
  111. package/v2/ops/color.mjs +0 -277
  112. package/v2/ops/crop.mjs +0 -64
  113. package/v2/ops/fill.mjs +0 -33
  114. package/v2/ops/flip.mjs +0 -33
  115. package/v2/ops/index.mjs +0 -9
  116. package/v2/ops/iterator.mjs +0 -34
  117. package/v2/ops/overlay.mjs +0 -63
  118. package/v2/ops/resize.mjs +0 -166
  119. package/v2/ops/rotate.mjs +0 -117
  120. package/v2/util/mem.mjs +0 -24
  121. package/wasm/any/font.wasm +0 -0
  122. package/wasm/any/gif.wasm +0 -0
  123. package/wasm/any/jpeg.wasm +0 -0
  124. package/wasm/any/png.wasm +0 -0
  125. package/wasm/any/svg.wasm +0 -0
  126. package/wasm/any/tiff.wasm +0 -0
  127. package/wasm/node/font.js +0 -142
  128. package/wasm/node/gif.js +0 -76
  129. package/wasm/node/jpeg.js +0 -92
  130. package/wasm/node/png.js +0 -49
  131. package/wasm/node/svg.js +0 -56
  132. package/wasm/node/tiff.js +0 -47
@@ -0,0 +1,67 @@
1
+ export interface WebPFrame {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ /** milliseconds */
7
+ duration: number;
8
+ /** whether the frame is alpha blended onto the canvas (false: it replaces the covered area) */
9
+ blend: boolean;
10
+ /** whether the frame's area is cleared to transparent after it was shown */
11
+ dispose: boolean;
12
+ /** RGBA */
13
+ pixels: Uint8Array;
14
+ }
15
+ export interface WebPAnimation {
16
+ width: number;
17
+ height: number;
18
+ /** 0 = forever */
19
+ loopCount: number;
20
+ /** ARGB background color hint (decoders, like browsers, are free to ignore it) */
21
+ background: number;
22
+ frames: WebPFrame[];
23
+ }
24
+ /** Whether the WebP is animated */
25
+ export declare function isAnimated(data: Uint8Array): boolean;
26
+ /**
27
+ * Decodes a still WebP into RGBA pixels. Animated WebPs decode to their first frame composited on the canvas
28
+ */
29
+ export declare function decode(data: Uint8Array): {
30
+ width: number;
31
+ height: number;
32
+ pixels: Uint8Array;
33
+ orientation: number;
34
+ };
35
+ /**
36
+ * Decodes an animated WebP into its frames (not composited); still WebPs become a single frame
37
+ */
38
+ export declare function decodeAnimation(data: Uint8Array, onlyFirstFrame?: boolean): WebPAnimation;
39
+ /**
40
+ * Composites animation frames onto the canvas like browsers do (transparent background, the background color hint
41
+ * is ignored as the spec allows)
42
+ */
43
+ export declare function composite({ width, height, frames }: WebPAnimation): Array<{
44
+ pixels: Uint8Array;
45
+ duration: number;
46
+ }>;
47
+ /**
48
+ * Encodes RGBA pixels into a lossless WebP
49
+ */
50
+ export declare function encode(rgba: Uint8Array, width: number, height: number): Uint8Array;
51
+ export interface WebPEncodeFrame {
52
+ x: number;
53
+ y: number;
54
+ width: number;
55
+ height: number;
56
+ /** milliseconds */
57
+ duration: number;
58
+ /** whether the frame's area is cleared after it was shown */
59
+ dispose: boolean;
60
+ /** RGBA, alpha blended onto the canvas */
61
+ pixels: Uint8Array;
62
+ }
63
+ /**
64
+ * Encodes frames into a lossless animated WebP. Frame offsets have to be even (the format stores them halved)
65
+ * @param plays How often to play the animation, 0 = forever
66
+ */
67
+ export declare function encodeAnimation(frames: WebPEncodeFrame[], width: number, height: number, plays: number): Uint8Array;
@@ -0,0 +1,280 @@
1
+ /*!
2
+ * WebP container. Animation frames are blended with libwebp's integer arithmetic (Copyright (c) 2010 Google Inc., BSD
3
+ * license, see THIRD_PARTY_NOTICES.md)
4
+ */
5
+ import * as vp8l from "./vp8l.js";
6
+ import * as vp8 from "./vp8.js";
7
+ import { orientation as exifOrientation } from "./exif.js";
8
+ const fourCC = (s) => s.charCodeAt(0) | s.charCodeAt(1) << 8 | s.charCodeAt(2) << 16 | s.charCodeAt(3) << 24;
9
+ const RIFF = fourCC('RIFF'), WEBP = fourCC('WEBP');
10
+ const VP8 = fourCC('VP8 '), VP8L = fourCC('VP8L'), VP8X = fourCC('VP8X');
11
+ const ALPH = fourCC('ALPH'), ANIM = fourCC('ANIM'), ANMF = fourCC('ANMF'), EXIF = fourCC('EXIF');
12
+ function chunks(data, start, end) {
13
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
14
+ const out = [];
15
+ let offset = start;
16
+ while (offset + 8 <= end) {
17
+ const type = view.getUint32(offset, true);
18
+ const size = view.getUint32(offset + 4, true);
19
+ const dataStart = offset + 8;
20
+ out.push({ type, data: data.subarray(dataStart, Math.min(end, dataStart + size)) });
21
+ offset = dataStart + size + (size & 1);
22
+ }
23
+ return out;
24
+ }
25
+ function container(data) {
26
+ if (data.length < 12)
27
+ throw new Error('WebP: invalid header');
28
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
29
+ if (view.getUint32(0, true) !== RIFF || view.getUint32(8, true) !== WEBP)
30
+ throw new Error('WebP: invalid header');
31
+ const size = view.getUint32(4, true);
32
+ return chunks(data, 12, Math.min(data.length, 8 + size));
33
+ }
34
+ const u24 = (d, o) => d[o] | d[o + 1] << 8 | d[o + 2] << 16;
35
+ function decodeBitstream(list) {
36
+ let alpha = null;
37
+ for (const chunk of list) {
38
+ if (chunk.type === ALPH)
39
+ alpha ??= chunk;
40
+ else if (chunk.type === VP8L) {
41
+ const { width, height, argb } = vp8l.decode(chunk.data);
42
+ const pixels = new Uint8Array(width * height * 4);
43
+ const out = new DataView(pixels.buffer);
44
+ for (let i = 0; i < argb.length; i++) {
45
+ const p = argb[i];
46
+ out.setUint32(i * 4, (p << 8 | p >>> 24) >>> 0);
47
+ }
48
+ return { width, height, pixels };
49
+ }
50
+ else if (chunk.type === VP8) {
51
+ const image = vp8.decode(chunk.data);
52
+ if (alpha)
53
+ applyAlpha(image.pixels, image.width, image.height, alpha.data);
54
+ return image;
55
+ }
56
+ }
57
+ throw new Error('WebP: no image data');
58
+ }
59
+ function applyAlpha(rgba, width, height, chunk) {
60
+ if (!chunk.length)
61
+ return;
62
+ const header = chunk[0];
63
+ const compression = header & 3, filter = header >> 2 & 3;
64
+ const total = width * height;
65
+ let alpha;
66
+ if (compression === 0) {
67
+ alpha = new Uint8Array(total);
68
+ alpha.set(chunk.subarray(1, 1 + total));
69
+ }
70
+ else if (compression === 1) {
71
+ const argb = vp8l.decodeStream(new vp8l.BitReader(chunk, 1), width, height);
72
+ alpha = new Uint8Array(total);
73
+ for (let i = 0; i < total; i++)
74
+ alpha[i] = argb[i] >> 8 & 255;
75
+ }
76
+ else
77
+ throw new Error('WebP: invalid alpha compression');
78
+ if (filter)
79
+ unfilterAlpha(alpha, width, height, filter);
80
+ for (let i = 0; i < total; i++)
81
+ rgba[i * 4 + 3] = alpha[i];
82
+ }
83
+ function unfilterAlpha(a, width, height, filter) {
84
+ for (let y = 0; y < height; y++) {
85
+ const row = y * width;
86
+ for (let x = 0; x < width; x++) {
87
+ const i = row + x;
88
+ let prediction;
89
+ if (x === 0 && y === 0)
90
+ prediction = 0;
91
+ else if (y === 0)
92
+ prediction = a[i - 1];
93
+ else if (x === 0)
94
+ prediction = a[i - width];
95
+ else if (filter === 1)
96
+ prediction = a[i - 1];
97
+ else if (filter === 2)
98
+ prediction = a[i - width];
99
+ else {
100
+ const g = a[i - 1] + a[i - width] - a[i - width - 1];
101
+ prediction = g < 0 ? 0 : g > 255 ? 255 : g;
102
+ }
103
+ a[i] = a[i] + prediction & 255;
104
+ }
105
+ }
106
+ }
107
+ export function isAnimated(data) {
108
+ const list = container(data);
109
+ const header = list[0];
110
+ return header && header.type === VP8X && header.data.length >= 10 && (header.data[0] & 2) !== 0 && list.some(c => c.type === ANMF);
111
+ }
112
+ export function decode(data) {
113
+ const list = container(data);
114
+ if (!list.length)
115
+ throw new Error('WebP: no image data');
116
+ let orientation = 1;
117
+ const exif = list.find(c => c.type === EXIF);
118
+ if (exif)
119
+ orientation = exifOrientation(exif.data);
120
+ if (list.some(c => c.type === ANMF)) {
121
+ const animation = decodeAnimation(data, true);
122
+ const frame = composite(animation)[0];
123
+ return { width: animation.width, height: animation.height, pixels: frame.pixels, orientation };
124
+ }
125
+ const image = decodeBitstream(list);
126
+ if (list[0].type === VP8X && list[0].data.length >= 10) {
127
+ const width = u24(list[0].data, 4) + 1, height = u24(list[0].data, 7) + 1;
128
+ if (width !== image.width || height !== image.height)
129
+ throw new Error('WebP: canvas and image sizes differ');
130
+ }
131
+ return { ...image, orientation };
132
+ }
133
+ export function decodeAnimation(data, onlyFirstFrame = false) {
134
+ const list = container(data);
135
+ const header = list[0];
136
+ if (!header || header.type !== VP8X || !list.some(c => c.type === ANMF)) {
137
+ const { width, height, pixels } = decodeBitstream(list);
138
+ return { width, height, loopCount: 0, background: 0, frames: [{ x: 0, y: 0, width, height, duration: 0, blend: false, dispose: false, pixels }] };
139
+ }
140
+ if (header.data.length < 10)
141
+ throw new Error('WebP: invalid VP8X chunk');
142
+ const width = u24(header.data, 4) + 1, height = u24(header.data, 7) + 1;
143
+ let loopCount = 0, background = 0;
144
+ const frames = [];
145
+ for (const chunk of list) {
146
+ if (chunk.type === ANIM && chunk.data.length >= 6) {
147
+ const d = chunk.data;
148
+ background = (d[3] << 24 | d[2] << 16 | d[1] << 8 | d[0]) >>> 0;
149
+ loopCount = d[4] | d[5] << 8;
150
+ }
151
+ else if (chunk.type === ANMF) {
152
+ const d = chunk.data;
153
+ if (d.length < 16)
154
+ throw new Error('WebP: invalid ANMF chunk');
155
+ const frame = decodeBitstream(chunks(d, 16, d.length));
156
+ frames.push({
157
+ x: u24(d, 0) * 2,
158
+ y: u24(d, 3) * 2,
159
+ width: frame.width,
160
+ height: frame.height,
161
+ duration: u24(d, 12),
162
+ blend: (d[15] & 2) === 0,
163
+ dispose: (d[15] & 1) === 1,
164
+ pixels: frame.pixels
165
+ });
166
+ if (onlyFirstFrame)
167
+ break;
168
+ }
169
+ }
170
+ if (!frames.length)
171
+ throw new Error('WebP: animation without frames');
172
+ return { width, height, loopCount, background, frames };
173
+ }
174
+ export function composite({ width, height, frames }) {
175
+ const canvas = new Uint8Array(width * height * 4);
176
+ const output = [];
177
+ for (const frame of frames) {
178
+ const x0 = Math.min(frame.x, width), x1 = Math.min(frame.x + frame.width, width);
179
+ const y0 = Math.min(frame.y, height), y1 = Math.min(frame.y + frame.height, height);
180
+ for (let y = y0; y < y1; y++)
181
+ for (let x = x0; x < x1; x++) {
182
+ const s = ((y - frame.y) * frame.width + x - frame.x) * 4, d = (y * width + x) * 4;
183
+ const sa = frame.pixels[s + 3];
184
+ if (!frame.blend || sa === 255 || canvas[d + 3] === 0) {
185
+ canvas[d] = frame.pixels[s];
186
+ canvas[d + 1] = frame.pixels[s + 1];
187
+ canvas[d + 2] = frame.pixels[s + 2];
188
+ canvas[d + 3] = sa;
189
+ }
190
+ else if (sa !== 0)
191
+ blendOver(frame.pixels, s, canvas, d);
192
+ }
193
+ output.push({ pixels: canvas.slice(), duration: frame.duration });
194
+ if (frame.dispose)
195
+ for (let y = y0; y < y1; y++)
196
+ canvas.fill(0, (y * width + x0) * 4, (y * width + x1) * 4);
197
+ }
198
+ return output;
199
+ }
200
+ function blendOver(src, s, dst, d) {
201
+ const sa = src[s + 3], da = dst[d + 3];
202
+ const dstFactor = da * (256 - sa) >> 8;
203
+ const alpha = sa + dstFactor;
204
+ const scale = alpha ? Math.floor((1 << 24) / alpha) : 0;
205
+ for (let c = 0; c < 3; c++) {
206
+ const blended = src[s + c] * sa + dst[d + c] * dstFactor;
207
+ dst[d + c] = blended * scale >>> 24;
208
+ }
209
+ dst[d + 3] = alpha;
210
+ }
211
+ function riffChunk(type, body) {
212
+ const out = new Uint8Array(8 + body.length + (body.length & 1));
213
+ const view = new DataView(out.buffer);
214
+ view.setUint32(0, fourCC(type), true);
215
+ view.setUint32(4, body.length, true);
216
+ out.set(body, 8);
217
+ return out;
218
+ }
219
+ function riff(chunks) {
220
+ let size = 4;
221
+ for (const c of chunks)
222
+ size += c.length;
223
+ const out = new Uint8Array(8 + size);
224
+ const view = new DataView(out.buffer);
225
+ view.setUint32(0, RIFF, true);
226
+ view.setUint32(4, size, true);
227
+ view.setUint32(8, WEBP, true);
228
+ let offset = 12;
229
+ for (const c of chunks) {
230
+ out.set(c, offset);
231
+ offset += c.length;
232
+ }
233
+ return out;
234
+ }
235
+ function toARGB(rgba, count) {
236
+ const argb = new Uint32Array(count);
237
+ for (let i = 0, o = 0; i < count; i++, o += 4)
238
+ argb[i] = (rgba[o + 3] << 24 | rgba[o] << 16 | rgba[o + 1] << 8 | rgba[o + 2]) >>> 0;
239
+ return argb;
240
+ }
241
+ const set24 = (d, o, v) => { d[o] = v & 255; d[o + 1] = v >> 8 & 255; d[o + 2] = v >> 16 & 255; };
242
+ export function encode(rgba, width, height) {
243
+ return riff([riffChunk('VP8L', vp8l.encode(toARGB(rgba, width * height), width, height))]);
244
+ }
245
+ export function encodeAnimation(frames, width, height, plays) {
246
+ if (!frames.length)
247
+ throw new Error('WebP: an animation needs at least one frame');
248
+ if (width > 1 << 24 || height > 1 << 24)
249
+ throw new RangeError('WebP: canvas too large');
250
+ let alpha = false;
251
+ for (const f of frames)
252
+ for (let i = 3; i < f.pixels.length && !alpha; i += 4)
253
+ if (f.pixels[i] !== 255)
254
+ alpha = true;
255
+ const vp8x = new Uint8Array(10);
256
+ vp8x[0] = 2 | (alpha ? 16 : 0);
257
+ set24(vp8x, 4, width - 1);
258
+ set24(vp8x, 7, height - 1);
259
+ const anim = new Uint8Array(6);
260
+ anim[4] = plays & 255;
261
+ anim[5] = plays >> 8 & 255;
262
+ const chunks = [riffChunk('VP8X', vp8x), riffChunk('ANIM', anim)];
263
+ for (const f of frames) {
264
+ if (f.x & 1 || f.y & 1)
265
+ throw new RangeError('WebP: frame offsets have to be even');
266
+ const bitstream = riffChunk('VP8L', vp8l.encode(toARGB(f.pixels, f.width * f.height), f.width, f.height));
267
+ const header = new Uint8Array(16);
268
+ set24(header, 0, f.x >> 1);
269
+ set24(header, 3, f.y >> 1);
270
+ set24(header, 6, f.width - 1);
271
+ set24(header, 9, f.height - 1);
272
+ set24(header, 12, Math.min(0xffffff, Math.max(0, Math.round(f.duration))));
273
+ header[15] = f.dispose ? 1 : 0;
274
+ const body = new Uint8Array(16 + bitstream.length);
275
+ body.set(header);
276
+ body.set(bitstream, 16);
277
+ chunks.push(riffChunk('ANMF', body));
278
+ }
279
+ return riff(chunks);
280
+ }
@@ -0,0 +1,22 @@
1
+ /*!
2
+ * zlib deflate/inflate, derived from fflate (https://github.com/101arrowz/fflate)
3
+ * Copyright (c) 2020 Arjun Barrett, MIT License (see THIRD_PARTY_NOTICES.md)
4
+ */
5
+ /**
6
+ * Compresses data into a zlib stream
7
+ * @param level compression level 0-9
8
+ */
9
+ export declare function compress(data: Uint8Array, level: number): Uint8Array;
10
+ /**
11
+ * Decompresses a zlib stream into at most limit bytes
12
+ */
13
+ export declare function decompress(data: Uint8Array, limit: number): Uint8Array;
14
+ /**
15
+ * Compresses data into a zlib stream, with the runtime's native zlib when available
16
+ * @param level compression level 0-9
17
+ */
18
+ export declare function compressAsync(data: Uint8Array, level: number): Promise<Uint8Array>;
19
+ /**
20
+ * Decompresses a zlib stream into at most limit bytes, using the runtime's native zlib when available
21
+ */
22
+ export declare function decompressAsync(data: Uint8Array, limit: number): Promise<Uint8Array>;