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.
- package/README.md +29 -9
- package/THIRD_PARTY_NOTICES.md +82 -0
- package/dist/animation.d.ts +140 -0
- package/dist/animation.js +197 -0
- package/dist/codecs/exif.js +62 -0
- package/dist/codecs/gif.js +32 -19
- package/dist/codecs/jpeg.js +13 -1
- package/dist/codecs/png.js +348 -62
- package/dist/codecs/quantize.js +2 -2
- package/dist/codecs/tga.js +246 -0
- package/dist/codecs/vp8-tables.js +17 -0
- package/dist/codecs/vp8.js +711 -0
- package/dist/codecs/vp8l.js +906 -0
- package/dist/codecs/webp.js +293 -0
- package/dist/frame.d.ts +40 -12
- package/dist/frame.js +20 -9
- package/dist/gif.d.ts +16 -26
- package/dist/gif.js +10 -81
- package/dist/image-type.d.ts +6 -1
- package/dist/image-type.js +17 -1
- package/dist/image.d.ts +262 -30
- package/dist/image.js +144 -45
- package/dist/index.d.ts +20 -6
- package/dist/index.js +13 -4
- package/dist/metadata.d.ts +35 -0
- package/dist/metadata.js +43 -0
- package/dist/render/font.js +99 -2
- package/dist/render/hinting.js +1236 -0
- package/dist/render/svg.js +39 -21
- package/dist/render/text.js +32 -7
- package/dist/util/optimize.js +264 -0
- package/dist/util/options.js +3 -0
- package/dist/util/resample.js +44 -26
- package/dist/util/utf8.js +4 -51
- package/dist/version.js +1 -1
- package/licenses/FTL.txt +169 -0
- package/package.json +10 -3
- package/dist/codecs/crc.d.ts +0 -9
- package/dist/codecs/exif.d.ts +0 -11
- package/dist/codecs/gif.d.ts +0 -44
- package/dist/codecs/jpeg.d.ts +0 -35
- package/dist/codecs/png.d.ts +0 -44
- package/dist/codecs/quantize.d.ts +0 -25
- package/dist/codecs/tiff.d.ts +0 -6
- package/dist/codecs/zlib.d.ts +0 -22
- package/dist/render/font.d.ts +0 -63
- package/dist/render/svg.d.ts +0 -11
- package/dist/render/text.d.ts +0 -61
- package/dist/util/blur.d.ts +0 -13
- package/dist/util/resample.d.ts +0 -12
- package/dist/util/utf8.d.ts +0 -5
- package/dist/version.d.ts +0 -1
package/dist/render/svg.js
CHANGED
|
@@ -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
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
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
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
}
|
package/dist/render/text.js
CHANGED
|
@@ -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
|
|
29
|
-
|
|
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) =>
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/util/resample.js
CHANGED
|
@@ -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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
plane[
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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]
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
export const VERSION = '3.2.0';
|