imagescript 3.0.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 (52) hide show
  1. package/README.md +29 -9
  2. package/THIRD_PARTY_NOTICES.md +82 -0
  3. package/dist/animation.d.ts +140 -0
  4. package/dist/animation.js +197 -0
  5. package/dist/codecs/exif.js +62 -0
  6. package/dist/codecs/gif.js +32 -19
  7. package/dist/codecs/jpeg.js +13 -1
  8. package/dist/codecs/png.js +348 -62
  9. package/dist/codecs/quantize.js +2 -2
  10. package/dist/codecs/tga.js +246 -0
  11. package/dist/codecs/vp8-tables.js +17 -0
  12. package/dist/codecs/vp8.js +711 -0
  13. package/dist/codecs/vp8l.js +906 -0
  14. package/dist/codecs/webp.js +293 -0
  15. package/dist/frame.d.ts +40 -12
  16. package/dist/frame.js +20 -9
  17. package/dist/gif.d.ts +16 -26
  18. package/dist/gif.js +10 -81
  19. package/dist/image-type.d.ts +6 -1
  20. package/dist/image-type.js +17 -1
  21. package/dist/image.d.ts +262 -30
  22. package/dist/image.js +144 -45
  23. package/dist/index.d.ts +20 -6
  24. package/dist/index.js +13 -4
  25. package/dist/metadata.d.ts +35 -0
  26. package/dist/metadata.js +43 -0
  27. package/dist/render/font.js +99 -2
  28. package/dist/render/hinting.js +1236 -0
  29. package/dist/render/svg.js +39 -21
  30. package/dist/render/text.js +32 -7
  31. package/dist/util/optimize.js +264 -0
  32. package/dist/util/options.js +3 -0
  33. package/dist/util/resample.js +44 -26
  34. package/dist/util/utf8.js +4 -51
  35. package/dist/version.js +1 -1
  36. package/licenses/FTL.txt +169 -0
  37. package/package.json +10 -3
  38. package/dist/codecs/crc.d.ts +0 -9
  39. package/dist/codecs/exif.d.ts +0 -11
  40. package/dist/codecs/gif.d.ts +0 -44
  41. package/dist/codecs/jpeg.d.ts +0 -35
  42. package/dist/codecs/png.d.ts +0 -44
  43. package/dist/codecs/quantize.d.ts +0 -25
  44. package/dist/codecs/tiff.d.ts +0 -6
  45. package/dist/codecs/zlib.d.ts +0 -22
  46. package/dist/render/font.d.ts +0 -63
  47. package/dist/render/svg.d.ts +0 -11
  48. package/dist/render/text.d.ts +0 -61
  49. package/dist/util/blur.d.ts +0 -13
  50. package/dist/util/resample.d.ts +0 -12
  51. package/dist/util/utf8.d.ts +0 -5
  52. package/dist/version.d.ts +0 -1
@@ -875,14 +875,25 @@ class Layer {
875
875
  width;
876
876
  height;
877
877
  data;
878
+ x0 = Infinity;
879
+ y0 = Infinity;
880
+ x1 = -Infinity;
881
+ y1 = -Infinity;
878
882
  constructor(width, height) {
879
883
  this.width = width;
880
884
  this.height = height;
881
885
  this.data = new Float32Array(width * height * 4);
882
886
  }
887
+ extend(x0, y0, x1, y1) {
888
+ this.x0 = Math.min(this.x0, x0);
889
+ this.y0 = Math.min(this.y0, y0);
890
+ this.x1 = Math.max(this.x1, x1);
891
+ this.y1 = Math.max(this.y1, y1);
892
+ }
883
893
  fill({ x0, y0, w, h, data }, paint) {
884
894
  const d = this.data;
885
895
  const solid = paint.solid;
896
+ this.extend(x0, y0, x0 + w, y0 + h);
886
897
  for (let y = 0; y < h; y++) {
887
898
  let i = ((y0 + y) * this.width + x0) * 4;
888
899
  for (let x = 0, o = y * w; x < w; x++, o++, i += 4) {
@@ -921,33 +932,40 @@ class Layer {
921
932
  }
922
933
  }
923
934
  composite(layer, opacity, mask) {
935
+ if (layer.x1 <= layer.x0)
936
+ return;
937
+ this.extend(layer.x0, layer.y0, layer.x1, layer.y1);
924
938
  const s = layer.data, d = this.data;
925
- for (let p = 0, i = 0; p < this.width * this.height; p++, i += 4) {
926
- if (s[i + 3] <= 0)
927
- continue;
928
- const f = mask ? opacity * mask[p] : opacity;
929
- if (f <= 0)
930
- continue;
931
- const a = s[i + 3] * f;
932
- const inv = 1 - a;
933
- d[i] = s[i] * f + d[i] * inv;
934
- d[i + 1] = s[i + 1] * f + d[i + 1] * inv;
935
- d[i + 2] = s[i + 2] * f + d[i + 2] * inv;
936
- d[i + 3] = a + d[i + 3] * inv;
939
+ for (let y = layer.y0; y < layer.y1; y++) {
940
+ for (let p = y * this.width + layer.x0, end = y * this.width + layer.x1, i = p * 4; p < end; p++, i += 4) {
941
+ if (s[i + 3] <= 0)
942
+ continue;
943
+ const f = mask ? opacity * mask[p] : opacity;
944
+ if (f <= 0)
945
+ continue;
946
+ const a = s[i + 3] * f;
947
+ const inv = 1 - a;
948
+ d[i] = s[i] * f + d[i] * inv;
949
+ d[i + 1] = s[i + 1] * f + d[i + 1] * inv;
950
+ d[i + 2] = s[i + 2] * f + d[i + 2] * inv;
951
+ d[i + 3] = a + d[i + 3] * inv;
952
+ }
937
953
  }
938
954
  }
939
955
  toRGBA() {
940
956
  const out = new Uint8ClampedArray(this.width * this.height * 4);
941
957
  const d = this.data;
942
- for (let i = 0; i < d.length; i += 4) {
943
- const a = d[i + 3];
944
- if (a <= 0)
945
- continue;
946
- const f = a >= 1 ? 255 : 255 / a;
947
- out[i] = d[i] * f;
948
- out[i + 1] = d[i + 1] * f;
949
- out[i + 2] = d[i + 2] * f;
950
- out[i + 3] = a * 255;
958
+ for (let y = this.y0; y < this.y1; y++) {
959
+ for (let i = (y * this.width + this.x0) * 4, end = (y * this.width + this.x1) * 4; i < end; i += 4) {
960
+ const a = d[i + 3];
961
+ if (a <= 0)
962
+ continue;
963
+ const f = a >= 1 ? 255 : 255 / a;
964
+ out[i] = d[i] * f;
965
+ out[i + 1] = d[i + 1] * f;
966
+ out[i + 2] = d[i + 2] * f;
967
+ out[i + 3] = a * 255;
968
+ }
951
969
  }
952
970
  return new Uint8Array(out.buffer);
953
971
  }
@@ -3,8 +3,10 @@
3
3
  * Copyright 2015 Google Inc., Apache License 2.0 (see THIRD_PARTY_NOTICES.md and licenses/Apache-2.0.txt)
4
4
  */
5
5
  import { Font } from "./font.js";
6
+ import { Hinter } from "./hinting.js";
6
7
  const SUBPIXELS = 4;
7
8
  const fonts = new WeakMap();
9
+ const hinters = new WeakMap();
8
10
  function loadFont(data) {
9
11
  let regions = fonts.get(data.buffer);
10
12
  if (!regions)
@@ -15,9 +17,18 @@ function loadFont(data) {
15
17
  regions.set(key, font = new Font(data));
16
18
  return font;
17
19
  }
18
- export function render(data, size, text, layout) {
20
+ export function render(data, size, text, layout, hinting = false) {
19
21
  const font = loadFont(data);
20
22
  const scale = size / font.unitsPerEm;
23
+ let hinter = null;
24
+ if (hinting && font.outlines === 'glyf') {
25
+ hinter = hinters.get(font) ?? null;
26
+ if (!hinter)
27
+ hinters.set(font, hinter = new Hinter(font));
28
+ if (!hinter.hasInstructions)
29
+ hinter = null;
30
+ }
31
+ hinting = hinter !== null;
21
32
  const ascent = Math.ceil(font.ascender * scale);
22
33
  const lineHeight = Math.ceil((font.ascender - font.descender + font.lineGap) * scale);
23
34
  const glyphs = new Map();
@@ -25,14 +36,23 @@ export function render(data, size, text, layout) {
25
36
  let g = glyphs.get(codepoint);
26
37
  if (!g) {
27
38
  const index = font.glyphIndex(codepoint);
28
- const path = font.outline(index);
29
- g = { index, advance: font.advance(index) * scale, ink: inkExtent(path, scale), bitmaps: [] };
39
+ const hinted = hinter && hinter.glyph(index, size);
40
+ if (hinted)
41
+ g = { index, path: hinted.path, scale: 1, advance: hinted.advance, ink: inkExtent(hinted.path, 1), bitmaps: [] };
42
+ else {
43
+ const path = font.outline(index);
44
+ const advance = font.advance(index) * scale;
45
+ g = { index, path, scale, advance: hinting ? Math.round(advance) : advance, ink: inkExtent(path, scale), bitmaps: [] };
46
+ }
30
47
  glyphs.set(codepoint, g);
31
48
  }
32
49
  return g;
33
50
  };
34
51
  const kerning = font.kerning;
35
- const kern = kerning ? (left, right) => kerning(left.index, right.index) * scale : () => 0;
52
+ const kern = kerning ? (left, right) => {
53
+ const k = kerning(left.index, right.index) * scale;
54
+ return hinting ? Math.round(k) : k;
55
+ } : () => 0;
36
56
  const lines = wrap(text, layout, glyph, kern);
37
57
  let visible = lines;
38
58
  if (isFinite(layout.maxHeight)) {
@@ -47,8 +67,10 @@ export function render(data, size, text, layout) {
47
67
  const height = visible.length * lineHeight;
48
68
  const coverage = new Uint8Array(width * height);
49
69
  visible.forEach((line, n) => {
50
- const offset = layout.horizontalAlign === 'right' ? width - line.extent
70
+ let offset = layout.horizontalAlign === 'right' ? width - line.extent
51
71
  : layout.horizontalAlign === 'center' ? (width - line.extent) / 2 : 0;
72
+ if (hinting)
73
+ offset = Math.round(offset);
52
74
  const baseline = ascent + n * lineHeight;
53
75
  for (const { x, glyph: g } of line.glyphs) {
54
76
  if (!g.ink)
@@ -57,7 +79,7 @@ export function render(data, size, text, layout) {
57
79
  const pixel = Math.floor(position);
58
80
  const subpixel = Math.round((position - pixel) * SUBPIXELS);
59
81
  if (g.bitmaps[subpixel] === undefined)
60
- g.bitmaps[subpixel] = rasterizeGlyph(font.outline(g.index), scale, subpixel / SUBPIXELS);
82
+ g.bitmaps[subpixel] = rasterizeGlyph(g.path, g.scale, subpixel / SUBPIXELS);
61
83
  const bitmap = g.bitmaps[subpixel];
62
84
  if (bitmap)
63
85
  blit(coverage, width, height, bitmap, pixel + bitmap.left, baseline - bitmap.top);
@@ -217,6 +239,8 @@ export class Rasterizer {
217
239
  line(x0, y0, x1, y1) {
218
240
  if (y0 === y1)
219
241
  return;
242
+ x0 = x0 < 0 ? 0 : x0 > this.width ? this.width : x0;
243
+ x1 = x1 < 0 ? 0 : x1 > this.width ? this.width : x1;
220
244
  let dir = 1;
221
245
  if (y0 > y1) {
222
246
  dir = -1;
@@ -232,7 +256,8 @@ export class Rasterizer {
232
256
  for (let y = Math.max(0, y0 | 0); y < yEnd; y++) {
233
257
  const lineStart = y * w;
234
258
  const dy = Math.min(y + 1, y1) - Math.max(y, y0);
235
- const xNext = x + dxdy * dy;
259
+ let xNext = x + dxdy * dy;
260
+ xNext = xNext < 0 ? 0 : xNext > w ? w : xNext;
236
261
  const d = dy * dir;
237
262
  const xa = x < xNext ? x : xNext, xb = x < xNext ? xNext : x;
238
263
  const xaFloor = Math.floor(xa);
@@ -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.0.0';
1
+ export const VERSION = '3.2.0';