imagescript 3.1.0 → 3.2.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 (51) hide show
  1. package/README.md +12 -4
  2. package/dist/animation.d.ts +92 -7
  3. package/dist/animation.js +49 -19
  4. package/dist/codecs/exif.js +62 -0
  5. package/dist/codecs/gif.js +32 -19
  6. package/dist/codecs/jpeg.js +13 -1
  7. package/dist/codecs/png.js +85 -21
  8. package/dist/codecs/quantize.js +2 -2
  9. package/dist/codecs/tga.js +47 -4
  10. package/dist/codecs/vp8l.js +27 -30
  11. package/dist/codecs/webp.js +23 -10
  12. package/dist/frame.d.ts +30 -2
  13. package/dist/frame.js +20 -9
  14. package/dist/gif.d.ts +10 -7
  15. package/dist/gif.js +7 -5
  16. package/dist/image-type.d.ts +1 -0
  17. package/dist/image-type.js +1 -0
  18. package/dist/image.d.ts +231 -31
  19. package/dist/image.js +111 -50
  20. package/dist/index.d.ts +14 -4
  21. package/dist/index.js +6 -4
  22. package/dist/metadata.d.ts +35 -0
  23. package/dist/metadata.js +43 -0
  24. package/dist/render/svg.js +39 -21
  25. package/dist/util/optimize.js +264 -0
  26. package/dist/util/options.js +3 -0
  27. package/dist/util/resample.js +44 -26
  28. package/dist/util/utf8.js +4 -51
  29. package/dist/version.js +1 -1
  30. package/package.json +9 -2
  31. package/dist/codecs/crc.d.ts +0 -9
  32. package/dist/codecs/exif.d.ts +0 -11
  33. package/dist/codecs/gif.d.ts +0 -44
  34. package/dist/codecs/jpeg.d.ts +0 -35
  35. package/dist/codecs/png.d.ts +0 -97
  36. package/dist/codecs/quantize.d.ts +0 -25
  37. package/dist/codecs/tga.d.ts +0 -16
  38. package/dist/codecs/tiff.d.ts +0 -6
  39. package/dist/codecs/vp8-tables.d.ts +0 -10
  40. package/dist/codecs/vp8.d.ts +0 -8
  41. package/dist/codecs/vp8l.d.ts +0 -62
  42. package/dist/codecs/webp.d.ts +0 -67
  43. package/dist/codecs/zlib.d.ts +0 -22
  44. package/dist/render/font.d.ts +0 -92
  45. package/dist/render/hinting.d.ts +0 -110
  46. package/dist/render/svg.d.ts +0 -11
  47. package/dist/render/text.d.ts +0 -63
  48. package/dist/util/blur.d.ts +0 -13
  49. package/dist/util/resample.d.ts +0 -12
  50. package/dist/util/utf8.d.ts +0 -5
  51. package/dist/version.d.ts +0 -1
@@ -0,0 +1,264 @@
1
+ const ALPHA = new Int32Array(new Uint8Array([0, 0, 0, 255]).buffer)[0];
2
+ const ALPHA_COST = 4 / 3;
3
+ export function optimize(frames, width, height, { replace, even, alpha, palette, cover }) {
4
+ const size = width * height * 4;
5
+ const canvas = new Uint8Array(size), canvas32 = new Int32Array(canvas.buffer);
6
+ const shown = new Uint8Array(size), shown32 = new Int32Array(shown.buffer);
7
+ const output = [];
8
+ const state = { canvas32, shown32, width, replace };
9
+ let opaque = true, denseTotal = 0, chosenTotal = 0;
10
+ const add = (duration) => {
11
+ let changed = bounds(state, { x: 0, y: 0, width, height });
12
+ if (!changed) {
13
+ if (output.length)
14
+ output[output.length - 1].duration += duration;
15
+ else {
16
+ output.push(empty(duration));
17
+ opaque = false;
18
+ }
19
+ return;
20
+ }
21
+ if (even)
22
+ changed = evenArea(changed);
23
+ let area = sparse(state, changed);
24
+ let blend = true;
25
+ if (area.unblendable && replace)
26
+ blend = false;
27
+ else if (area.unblendable) {
28
+ const last = output[output.length - 1];
29
+ grow(last, union(last, area.unblendable));
30
+ last.dispose = true;
31
+ clear(shown, width, last);
32
+ changed = bounds(state, { x: 0, y: 0, width, height });
33
+ if (!changed)
34
+ return void output.push(empty(duration));
35
+ if (even)
36
+ changed = evenArea(changed);
37
+ area = sparse(state, changed);
38
+ }
39
+ const { x, y, width: w, height: h } = changed;
40
+ const pixels = area.pixels;
41
+ if (!output.length && (w !== width || h !== height))
42
+ opaque = false;
43
+ if (!area.denseOpaque)
44
+ opaque = false;
45
+ const sparseCost = alpha === 'frame' && area.denseOpaque ? area.sparseChanges * ALPHA_COST : area.sparseChanges;
46
+ const dense = !blend || area.denseChanges < sparseCost
47
+ || (palette !== undefined && colors(new Int32Array(pixels.buffer), w, { x: 0, y: 0, width: w, height: h }, palette + 1) > palette
48
+ && colors(canvas32, width, changed, palette + 1) <= palette);
49
+ denseTotal += area.denseChanges;
50
+ chosenTotal += dense ? area.denseChanges : area.sparseChanges;
51
+ if (dense) {
52
+ for (let row = 0; row < h; row++)
53
+ pixels.set(canvas.subarray(((y + row) * width + x) * 4, ((y + row) * width + x + w) * 4), row * w * 4);
54
+ if (replace)
55
+ blend = false;
56
+ }
57
+ for (let row = 0; row < h; row++) {
58
+ const start = ((y + row) * width + x) * 4;
59
+ shown.set(canvas.subarray(start, start + w * 4), start);
60
+ }
61
+ output.push({ x, y, width: w, height: h, duration, dispose: false, blend, pixels });
62
+ };
63
+ let cleared = { x: 0, y: 0, width, height };
64
+ for (const f of frames) {
65
+ const saved = f.dispose === 2 ? canvas.slice() : null;
66
+ const area = clip(f, width, height);
67
+ if (cleared && contains(cleared, area))
68
+ copy(canvas, width, f, area);
69
+ else
70
+ draw(canvas, width, height, f);
71
+ cleared = null;
72
+ add(f.duration);
73
+ if (f.dispose === 1) {
74
+ clear(canvas, width, area);
75
+ cleared = area;
76
+ }
77
+ else if (saved)
78
+ canvas.set(saved);
79
+ }
80
+ if (typeof alpha === 'function' && opaque && output.some(f => f.blend) && denseTotal < chosenTotal * alpha(output)) {
81
+ shown.fill(0);
82
+ for (const f of output) {
83
+ const pixels32 = new Int32Array(f.pixels.buffer, f.pixels.byteOffset, f.width * f.height);
84
+ for (let row = 0, k = 0; row < f.height; row++, k += f.width) {
85
+ const start = (f.y + row) * width + f.x;
86
+ if (!f.blend)
87
+ shown32.set(pixels32.subarray(k, k + f.width), start);
88
+ else
89
+ for (let i = k, p = start, end = k + f.width; i < end; i++, p++) {
90
+ if ((pixels32[i] & ALPHA) === 0)
91
+ pixels32[i] = shown32[p];
92
+ else
93
+ shown32[p] = pixels32[i];
94
+ }
95
+ }
96
+ f.blend = false;
97
+ }
98
+ }
99
+ if (cover && output.length)
100
+ grow(output[0], { x: 0, y: 0, width, height });
101
+ return output;
102
+ }
103
+ function colors(image32, width, { x, y, width: w, height: h }, limit) {
104
+ const seen = new Set();
105
+ for (let row = y; row < y + h; row++) {
106
+ let previous;
107
+ for (let p = row * width + x, end = p + w; p < end; p++) {
108
+ const value = image32[p];
109
+ if (value === previous)
110
+ continue;
111
+ previous = value;
112
+ seen.add(value);
113
+ if (seen.size >= limit)
114
+ return seen.size;
115
+ }
116
+ }
117
+ return seen.size;
118
+ }
119
+ const empty = (duration) => ({ x: 0, y: 0, width: 1, height: 1, duration, dispose: false, blend: true, pixels: new Uint8Array(4) });
120
+ function clip(r, width, height) {
121
+ const x = Math.max(0, r.x), y = Math.max(0, r.y);
122
+ return { x, y, width: Math.max(0, Math.min(width, r.x + r.width) - x), height: Math.max(0, Math.min(height, r.y + r.height) - y) };
123
+ }
124
+ function contains(a, b) {
125
+ return b.x >= a.x && b.y >= a.y && b.x + b.width <= a.x + a.width && b.y + b.height <= a.y + a.height;
126
+ }
127
+ function union(a, b) {
128
+ const x = Math.min(a.x, b.x), y = Math.min(a.y, b.y);
129
+ return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
130
+ }
131
+ function evenArea({ x, y, width, height }) {
132
+ return { x: x & ~1, y: y & ~1, width: width + (x & 1), height: height + (y & 1) };
133
+ }
134
+ function sparse({ canvas32, shown32, width, replace }, { x, y, width: w, height: h }) {
135
+ const pixels = new Uint8Array(w * h * 4), pixels32 = new Int32Array(pixels.buffer);
136
+ let sparseChanges = 0, denseChanges = 0, lastSparse = -1, lastDense = -1;
137
+ let left = w, right = -1, top = h, bottom = -1, translucent = 0;
138
+ for (let row = 0, k = 0; row < h; row++) {
139
+ const start = (y + row) * width + x;
140
+ for (let i = 0, p = start; i < w; i++, p++, k++) {
141
+ const value = canvas32[p], before = shown32[p];
142
+ if (value === before || ((value | before) & ALPHA) === 0) {
143
+ if (lastSparse !== 0)
144
+ sparseChanges++;
145
+ lastSparse = 0;
146
+ }
147
+ else {
148
+ pixels32[k] = value;
149
+ if (value !== lastSparse)
150
+ sparseChanges++;
151
+ lastSparse = value;
152
+ if ((value & ALPHA) !== ALPHA && (replace || (before & ALPHA) !== 0)) {
153
+ if (i < left)
154
+ left = i;
155
+ if (i > right)
156
+ right = i;
157
+ if (row < top)
158
+ top = row;
159
+ bottom = row;
160
+ }
161
+ }
162
+ if (value !== lastDense) {
163
+ denseChanges++;
164
+ translucent |= ~value & ALPHA;
165
+ }
166
+ lastDense = value;
167
+ }
168
+ }
169
+ const unblendable = right < 0 ? null : { x: x + left, y: y + top, width: right - left + 1, height: bottom - top + 1 };
170
+ return { pixels, sparseChanges, denseChanges, unblendable, denseOpaque: translucent === 0 };
171
+ }
172
+ function bounds({ canvas32, shown32, width }, area) {
173
+ const x0 = area.x, x1 = area.x + area.width, y0 = area.y, y1 = area.y + area.height;
174
+ const test = (p) => {
175
+ const c = canvas32[p], s = shown32[p];
176
+ return c !== s && ((c | s) & ALPHA) !== 0;
177
+ };
178
+ const rowHas = (y, from, to) => {
179
+ for (let x = from, p = y * width + from; x < to; x++, p++)
180
+ if (test(p))
181
+ return x;
182
+ return -1;
183
+ };
184
+ let top = y0, first = -1;
185
+ for (; top < y1; top++)
186
+ if ((first = rowHas(top, x0, x1)) >= 0)
187
+ break;
188
+ if (top === y1)
189
+ return null;
190
+ let bottom = y1 - 1;
191
+ while (bottom > top && rowHas(bottom, x0, x1) < 0)
192
+ bottom--;
193
+ let left = first, right = first;
194
+ for (let y = top; y <= bottom; y++) {
195
+ if (left > x0) {
196
+ const x = rowHas(y, x0, left);
197
+ if (x >= 0)
198
+ left = x;
199
+ }
200
+ for (let x = x1 - 1, p = y * width + x; x > right; x--, p--)
201
+ if (test(p)) {
202
+ right = x;
203
+ break;
204
+ }
205
+ }
206
+ return { x: left, y: top, width: right - left + 1, height: bottom - top + 1 };
207
+ }
208
+ function draw(canvas, width, height, f) {
209
+ const { x: x0, y: y0, width: w, height: h } = clip(f, width, height);
210
+ const pixels = f.pixels.byteOffset % 4 ? f.pixels.slice() : f.pixels;
211
+ const src = new Int32Array(pixels.buffer, pixels.byteOffset, f.width * f.height);
212
+ const dst = new Int32Array(canvas.buffer, canvas.byteOffset, width * height);
213
+ for (let y = y0; y < y0 + h; y++)
214
+ for (let x = x0, s = (y - f.y) * f.width + x - f.x, d = y * width + x; x < x0 + w; x++, s++, d++) {
215
+ let n = 0;
216
+ while (n < x0 + w - x && (src[s + n] & ALPHA) === ALPHA)
217
+ n++;
218
+ if (n) {
219
+ if (n >= 16)
220
+ dst.set(src.subarray(s, s + n), d);
221
+ else
222
+ for (let i = 0; i < n; i++)
223
+ dst[d + i] = src[s + i];
224
+ x += n - 1;
225
+ s += n - 1;
226
+ d += n - 1;
227
+ continue;
228
+ }
229
+ const value = src[s], alpha = value & ALPHA;
230
+ if (alpha === 0)
231
+ continue;
232
+ if (alpha === ALPHA || (dst[d] & ALPHA) === 0)
233
+ dst[d] = value;
234
+ else {
235
+ const sa = pixels[s * 4 + 3], da = canvas[d * 4 + 3];
236
+ const u = sa * 255, v = (255 - sa) * da, al = u + v;
237
+ for (let c = 0; c < 3; c++)
238
+ canvas[d * 4 + c] = (pixels[s * 4 + c] * u + canvas[d * 4 + c] * v) / al | 0;
239
+ canvas[d * 4 + 3] = al / 255 | 0;
240
+ }
241
+ }
242
+ }
243
+ function copy(canvas, width, f, { x, y, width: w, height: h }) {
244
+ const canvas32 = new Int32Array(canvas.buffer, canvas.byteOffset, width * (y + h));
245
+ for (let row = y; row < y + h; row++) {
246
+ const s = ((row - f.y) * f.width + x - f.x) * 4, d = row * width + x;
247
+ canvas.set(f.pixels.subarray(s, s + w * 4), d * 4);
248
+ for (let p = d; p < d + w; p++)
249
+ if ((canvas32[p] & ALPHA) === 0)
250
+ canvas32[p] = 0;
251
+ }
252
+ }
253
+ function clear(canvas, width, r) {
254
+ for (let y = r.y; y < r.y + r.height; y++)
255
+ canvas.fill(0, (y * width + r.x) * 4, (y * width + r.x + r.width) * 4);
256
+ }
257
+ function grow(frame, area) {
258
+ if (area.x === frame.x && area.y === frame.y && area.width === frame.width && area.height === frame.height)
259
+ return;
260
+ const pixels = new Uint8Array(area.width * area.height * 4);
261
+ for (let row = 0; row < frame.height; row++)
262
+ pixels.set(frame.pixels.subarray(row * frame.width * 4, (row + 1) * frame.width * 4), ((frame.y - area.y + row) * area.width + frame.x - area.x) * 4);
263
+ Object.assign(frame, area, { pixels });
264
+ }
@@ -0,0 +1,3 @@
1
+ export function isOptions(value) {
2
+ return typeof value === 'object' && value !== null && !ArrayBuffer.isView(value);
3
+ }
@@ -40,33 +40,33 @@ function coefficients(inSize, outSize, filter) {
40
40
  }
41
41
  export function resample(src, width, height, outWidth, outHeight, filter, out) {
42
42
  let plane = new Float32Array(outWidth * height * 4);
43
- const horizontal = outWidth !== width ? coefficients(width, outWidth, filter) : null;
44
- for (let y = 0; y < height; y++) {
45
- const row = y * width * 4;
46
- for (let x = 0; x < outWidth; x++) {
47
- let r = 0, g = 0, b = 0, a = 0;
48
- if (horizontal) {
49
- const { start, count, weights, size } = horizontal;
50
- for (let k = 0, s = row + start[x] * 4; k < count[x]; k++, s += 4) {
51
- const wa = weights[x * size + k] * src[s + 3];
43
+ if (outWidth !== width) {
44
+ const { start, count, weights, size } = coefficients(width, outWidth, filter);
45
+ for (let y = 0, o = 0; y < height; y++) {
46
+ const row = y * width * 4;
47
+ for (let x = 0; x < outWidth; x++, o += 4) {
48
+ let r = 0, g = 0, b = 0, a = 0;
49
+ for (let k = 0, s = row + start[x] * 4, w = x * size, n = count[x]; k < n; k++, s += 4) {
50
+ const wa = weights[w + k] * src[s + 3];
52
51
  r += src[s] * wa;
53
52
  g += src[s + 1] * wa;
54
53
  b += src[s + 2] * wa;
55
54
  a += wa;
56
55
  }
56
+ plane[o] = r / 255;
57
+ plane[o + 1] = g / 255;
58
+ plane[o + 2] = b / 255;
59
+ plane[o + 3] = a;
57
60
  }
58
- else {
59
- const s = row + x * 4;
60
- a = src[s + 3];
61
- r = src[s] * a;
62
- g = src[s + 1] * a;
63
- b = src[s + 2] * a;
64
- }
65
- const o = (y * outWidth + x) * 4;
66
- plane[o] = r / 255;
67
- plane[o + 1] = g / 255;
68
- plane[o + 2] = b / 255;
69
- plane[o + 3] = a;
61
+ }
62
+ }
63
+ else {
64
+ for (let s = 0; s < plane.length; s += 4) {
65
+ const a = src[s + 3];
66
+ plane[s] = src[s] * a / 255;
67
+ plane[s + 1] = src[s + 1] * a / 255;
68
+ plane[s + 2] = src[s + 2] * a / 255;
69
+ plane[s + 3] = a;
70
70
  }
71
71
  }
72
72
  if (outHeight !== height) {
@@ -74,12 +74,30 @@ export function resample(src, width, height, outWidth, outHeight, filter, out) {
74
74
  const next = new Float32Array(outWidth * outHeight * 4);
75
75
  const stride = outWidth * 4;
76
76
  for (let y = 0; y < outHeight; y++) {
77
- const o = y * stride;
78
- for (let k = 0; k < count[y]; k++) {
79
- const w = weights[y * size + k];
80
- const s = (start[y] + k) * stride;
77
+ const o = y * stride, n = count[y], w = y * size;
78
+ const s0 = start[y] * stride, s1 = s0 + stride, s2 = s1 + stride, s3 = s2 + stride;
79
+ const w0 = weights[w], w1 = weights[w + 1], w2 = weights[w + 2], w3 = weights[w + 3];
80
+ if (n === 1)
81
+ for (let x = 0; x < stride; x++)
82
+ next[o + x] = 0 + plane[s0 + x] * w0;
83
+ else if (n === 2)
84
+ for (let x = 0; x < stride; x++)
85
+ next[o + x] = Math.fround(0 + plane[s0 + x] * w0) + plane[s1 + x] * w1;
86
+ else if (n === 3) {
81
87
  for (let x = 0; x < stride; x++)
82
- next[o + x] += plane[s + x] * w;
88
+ next[o + x] = Math.fround(Math.fround(0 + plane[s0 + x] * w0) + plane[s1 + x] * w1) + plane[s2 + x] * w2;
89
+ }
90
+ else if (n === 4) {
91
+ for (let x = 0; x < stride; x++)
92
+ next[o + x] = Math.fround(Math.fround(Math.fround(0 + plane[s0 + x] * w0) + plane[s1 + x] * w1) + plane[s2 + x] * w2) + plane[s3 + x] * w3;
93
+ }
94
+ else {
95
+ for (let x = 0; x < stride; x++) {
96
+ let sum = 0;
97
+ for (let k = 0, s = s0 + x; k < n; k++, s += stride)
98
+ sum = Math.fround(sum + plane[s] * weights[w + k]);
99
+ next[o + x] = sum;
100
+ }
83
101
  }
84
102
  }
85
103
  plane = next;
package/dist/util/utf8.js CHANGED
@@ -1,55 +1,8 @@
1
+ const encoder = new TextEncoder();
2
+ const decoder = new TextDecoder();
1
3
  export function encode(text) {
2
- const out = [];
3
- for (const char of text) {
4
- const c = char.codePointAt(0);
5
- if (c < 0x80)
6
- out.push(c);
7
- else if (c < 0x800)
8
- out.push(0xc0 | c >> 6, 0x80 | c & 63);
9
- else if (c < 0x10000)
10
- out.push(0xe0 | c >> 12, 0x80 | c >> 6 & 63, 0x80 | c & 63);
11
- else
12
- out.push(0xf0 | c >> 18, 0x80 | c >> 12 & 63, 0x80 | c >> 6 & 63, 0x80 | c & 63);
13
- }
14
- return Uint8Array.from(out);
4
+ return encoder.encode(text);
15
5
  }
16
6
  export function decode(bytes) {
17
- let out = '';
18
- let i = bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf ? 3 : 0;
19
- while (i < bytes.length) {
20
- const b = bytes[i++];
21
- let c, extra;
22
- if (b < 0x80) {
23
- c = b;
24
- extra = 0;
25
- }
26
- else if (b >= 0xc2 && b < 0xe0) {
27
- c = b & 31;
28
- extra = 1;
29
- }
30
- else if (b >= 0xe0 && b < 0xf0) {
31
- c = b & 15;
32
- extra = 2;
33
- }
34
- else if (b >= 0xf0 && b < 0xf5) {
35
- c = b & 7;
36
- extra = 3;
37
- }
38
- else {
39
- out += '�';
40
- continue;
41
- }
42
- let valid = true;
43
- for (let k = 0; k < extra; k++) {
44
- const next = bytes[i];
45
- if (next === undefined || (next & 0xc0) !== 0x80) {
46
- valid = false;
47
- break;
48
- }
49
- c = c << 6 | next & 63;
50
- i++;
51
- }
52
- out += valid && c <= 0x10ffff ? String.fromCodePoint(c) : '�';
53
- }
54
- return out;
7
+ return decoder.decode(bytes);
55
8
  }
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '3.1.0';
1
+ export const VERSION = '3.2.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imagescript",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "zero-dependency javascript image manipulation",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -47,7 +47,14 @@
47
47
  "bitmap",
48
48
  "gif",
49
49
  "picture",
50
- "thumbnail"
50
+ "thumbnail",
51
+ "webp",
52
+ "apng",
53
+ "tiff",
54
+ "tga",
55
+ "animation",
56
+ "text rendering",
57
+ "font"
51
58
  ],
52
59
  "author": "Mathis Mensing <mathis@matmen.dev>",
53
60
  "license": "(AGPL-3.0-or-later OR MIT)",
@@ -1,9 +0,0 @@
1
- /*!
2
- * Slice-by-16 CRC-32, derived from "Fast CRC32 in JavaScript" (https://gist.github.com/101arrowz/e58695f7ccfdf74f60ba22018093edea)
3
- * Copyright (c) 2021 Arjun Barrett, MIT License (see THIRD_PARTY_NOTICES.md)
4
- */
5
- /**
6
- * CRC-32 (ISO 3309), as used by PNG and zlib. Processes 16 bytes per step (slice-by-16, see #12), byte by byte so
7
- * it doesn't depend on alignment or endianness
8
- */
9
- export declare function crc32(buffer: Uint8Array): number;
@@ -1,11 +0,0 @@
1
- /**
2
- * Reads the orientation tag (0x0112) from the first IFD of Exif data
3
- * @param data a TIFF header and IFDs, optionally preceded by "Exif\0\0" (as in JPEG's APP1)
4
- * @return the orientation (1-8), 1 if it is missing or invalid
5
- */
6
- export declare function orientation(data: Uint8Array): number;
7
- /**
8
- * Reads tiff:Orientation from an XMP packet, as attribute or element
9
- * @return the orientation (1-8), 1 if it is missing or invalid
10
- */
11
- export declare function xmpOrientation(xmp: string): number;
@@ -1,44 +0,0 @@
1
- export interface GIFFrame {
2
- x: number;
3
- y: number;
4
- width: number;
5
- height: number;
6
- /** Frame delay in centiseconds */
7
- delay: number;
8
- /** ImageScript disposal mode (0 any, 1 keep, 2 previous, 3 background) */
9
- dispose: number;
10
- /** RGBA pixel data */
11
- pixels: Uint8Array;
12
- }
13
- export interface GIFData {
14
- width: number;
15
- height: number;
16
- /** -1 loops forever, 0 plays once, n repeats n times */
17
- loopCount: number;
18
- frames: GIFFrame[];
19
- }
20
- /**
21
- * Encodes frames into a GIF
22
- * @param {{width: number, height: number, loopCount?: number, frames: GIFFrame[]}} gif loopCount -1 loops forever, 0 plays once, n repeats n times
23
- * @param {number} [quality=95] 1-100, scales the palette size (1 = 8 colors, 50 = 55, 90 and above = 256)
24
- * @return {Uint8Array}
25
- */
26
- export declare function encode({ width, height, loopCount, frames }: Omit<GIFData, 'loopCount'> & {
27
- loopCount?: number;
28
- }, quality?: number): Uint8Array;
29
- /**
30
- * Decodes a GIF into its individual (uncomposited) frames, zero-sized images are skipped
31
- * @param {Uint8Array} data
32
- * @param {boolean} [onlyFirstFrame=false]
33
- * @return {{width: number, height: number, loopCount: number, frames: GIFFrame[]}}
34
- */
35
- export declare function decode(data: Uint8Array, onlyFirstFrame?: boolean): GIFData;
36
- /**
37
- * Composites decoded frames onto the logical screen, applying their disposal modes
38
- * @param {{width: number, height: number, frames: GIFFrame[]}} gif output of {@link decode}
39
- * @return {Array<{pixels: Uint8Array, delay: number}>} full screen RGBA frames
40
- */
41
- export declare function composite({ width, height, frames }: GIFData): Array<{
42
- pixels: Uint8Array;
43
- delay: number;
44
- }>;
@@ -1,35 +0,0 @@
1
- /*!
2
- * JPEG codec. This software is based in part on the work of the Independent JPEG Group: the IDCT, FDCT, upsampling,
3
- * color conversion, progressive refinement and optimal Huffman tables are translated from libjpeg/libjpeg-turbo
4
- * (IJG license, see licenses/README.ijg). The scan decoder follows the structure of pdf.js' jpg.js
5
- * (Copyright 2014 Mozilla Foundation, Apache License 2.0). See THIRD_PARTY_NOTICES.md
6
- */
7
- export interface DecodeOptions {
8
- /** overrides the color transform detected from JFIF/Adobe markers (used by TIFF) */
9
- transform?: 'ycc' | 'rgb' | 'cmyk' | null;
10
- }
11
- /**
12
- * Decodes a JPEG into 8-bit RGBA pixel data
13
- * Supports baseline, extended and progressive huffman coded JPEGs with any
14
- * sampling factors, restart intervals and greyscale, YCbCr, RGB, CMYK and YCCK color
15
- * The output matches libjpeg's defaults (islow IDCT, fancy upsampling)
16
- * @param {Uint8Array} data
17
- * @param {{transform?: 'ycc'|'rgb'|'cmyk'}} [options] overrides the color transform detected from JFIF/Adobe markers (used by TIFF)
18
- * @return {{width: number, height: number, pixels: Uint8Array}}
19
- */
20
- export declare function decode(data: Uint8Array, { transform }?: DecodeOptions): {
21
- width: number;
22
- height: number;
23
- pixels: Uint8Array;
24
- orientation: number;
25
- };
26
- /**
27
- * Encodes RGBA pixel data into a baseline JPEG (alpha is ignored)
28
- * Uses 4:2:0 chroma subsampling up to quality 90 and 4:4:4 above, with optimized huffman tables
29
- * @param {Uint8Array} pixels
30
- * @param {number} width
31
- * @param {number} height
32
- * @param {number} [quality=90] 1-100
33
- * @return {Uint8Array}
34
- */
35
- export declare function encode(pixels: Uint8Array, width: number, height: number, quality?: number): Uint8Array;
@@ -1,97 +0,0 @@
1
- export interface EncodeOptions {
2
- /** tEXt chunks, empty values are skipped */
3
- text?: Record<string, string | undefined>;
4
- width: number;
5
- height: number;
6
- channels: number;
7
- depth?: number;
8
- level?: number;
9
- /** packed RGB palette, makes the data palette indices (1 channel) */
10
- palette?: Uint8Array;
11
- /** alpha per palette entry */
12
- transparency?: Uint8Array;
13
- /** whether to paeth filter the rows (pays off for truecolor and greyscale images) */
14
- filter?: boolean;
15
- }
16
- export interface Decoded {
17
- width: number;
18
- height: number;
19
- /** 8-bit RGBA */
20
- pixels: Uint8Array;
21
- /** Exif orientation (1-8) */
22
- orientation: number;
23
- }
24
- /**
25
- * Encodes raw pixel data into a PNG
26
- * @param data Raw pixel data, rows packed MSB first for depths below 8
27
- */
28
- export declare function encode(data: Uint8Array, { text, width, height, channels, depth, level, palette, transparency, filter: filterRows }: EncodeOptions): Promise<Uint8Array>;
29
- /**
30
- * Encodes 8-bit RGBA pixels into a PNG with the smallest lossless color type (see {@link pixelFormat}).
31
- * Rows of non-palette images are paeth filtered (unless the data isn't compressed), palette indices aren't
32
- * filtered (like libpng recommends)
33
- */
34
- export declare function encodeRGBA(rgba: Uint8Array, width: number, height: number, level: number, text?: EncodeOptions['text']): Promise<Uint8Array>;
35
- export interface APNGEncodeFrame {
36
- x: number;
37
- y: number;
38
- width: number;
39
- height: number;
40
- /** milliseconds (stored as a fraction of up to 65535/65535 seconds) */
41
- duration: number;
42
- /** 0 none, 1 background (transparent), 2 previous */
43
- dispose: number;
44
- /** RGBA */
45
- pixels: Uint8Array;
46
- }
47
- /**
48
- * Encodes frames into an APNG. Frames are alpha blended onto the canvas ("over"); the first frame is also the
49
- * default image that viewers without APNG support show
50
- * @param plays How often to play the animation, 0 = forever
51
- */
52
- export declare function encodeAnimation(frames: APNGEncodeFrame[], width: number, height: number, plays: number, level: number): Promise<Uint8Array>;
53
- /**
54
- * Decodes a PNG into 8-bit RGBA pixel data (for APNGs, the default image)
55
- * Supports all color types, bit depths, tRNS transparency and adam7 interlacing
56
- * The orientation comes from the eXIf chunk, or, if there is none, from the Exif text ImageMagick wrote before eXIf
57
- * existed ("Raw profile type exif") or from the XMP packet
58
- */
59
- export declare function decode(array: Uint8Array): Promise<Decoded>;
60
- export interface APNGFrame {
61
- x: number;
62
- y: number;
63
- width: number;
64
- height: number;
65
- /** milliseconds */
66
- duration: number;
67
- /** 0 none, 1 background (transparent), 2 previous */
68
- dispose: number;
69
- /** 0 source (replace), 1 over */
70
- blend: number;
71
- pixels: Uint8Array;
72
- }
73
- /** Whether the PNG is an APNG (has an acTL chunk) */
74
- export declare function isAnimated(array: Uint8Array): boolean;
75
- /**
76
- * Decodes an APNG (https://wiki.mozilla.org/APNG_Specification) into its (uncomposited) frames; a plain PNG becomes
77
- * a single frame
78
- * @return plays: 0 = forever
79
- */
80
- export declare function decodeAnimation(array: Uint8Array, onlyFirstFrame?: boolean): Promise<{
81
- width: number;
82
- height: number;
83
- plays: number;
84
- frames: APNGFrame[];
85
- }>;
86
- /**
87
- * Composites APNG frames onto the canvas, as the APNG specification describes (the integer "over" operation of its
88
- * blend_op section, https://wiki.mozilla.org/APNG_Specification)
89
- */
90
- export declare function compositeAnimation({ width, height, frames }: {
91
- width: number;
92
- height: number;
93
- frames: APNGFrame[];
94
- }): Array<{
95
- pixels: Uint8Array;
96
- duration: number;
97
- }>;
@@ -1,25 +0,0 @@
1
- /**
2
- * Reduces RGBA pixel data to an indexed palette
3
- * Pixels with alpha < 128 are mapped to a dedicated transparent index
4
- * @param {Uint8Array} pixels RGBA pixel data
5
- * @param {number} width Image width in pixels (needed for dithering)
6
- * @param {{maxColors?: number, quality?: number}} [options] quality (1-100) scales the palette size exponentially (1 = 8 colors, 50 = 55, 90 and above = 256)
7
- * @return {{palette: Uint8Array, indices: Uint8Array, transparentIndex: number}} palette is packed RGB, transparentIndex is -1 if unused
8
- */
9
- export interface QuantizeOptions {
10
- maxColors?: number;
11
- quality?: number;
12
- }
13
- export interface Quantized {
14
- /** packed RGB */
15
- palette: Uint8Array;
16
- indices: Uint8Array;
17
- /** -1 if no pixel is transparent */
18
- transparentIndex: number;
19
- }
20
- /**
21
- * Finds the color of the largest cluster among the opaque pixels that pass the filter
22
- * @return packed RGB, or null if no pixel qualifies
23
- */
24
- export declare function dominantColor(pixels: Uint8Array, include: (r: number, g: number, b: number) => boolean, clusters?: number): [number, number, number] | null;
25
- export declare function quantize(pixels: Uint8Array, width: number, { maxColors, quality }?: QuantizeOptions): Quantized;