@pixerate/editor 0.87.0 → 0.89.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/THIRD_PARTY_NOTICES.md +8 -2
- package/dist/agent/index.cjs +28 -28
- package/dist/agent/index.js +3 -3
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.js +16 -16
- package/dist/{chunk-TYOAJDFD.cjs → chunk-66IMF6OD.cjs} +3 -3
- package/dist/{chunk-TYOAJDFD.cjs.map → chunk-66IMF6OD.cjs.map} +1 -1
- package/dist/chunk-D4ZCU72J.js +604 -0
- package/dist/chunk-D4ZCU72J.js.map +1 -0
- package/dist/{chunk-IMSX4IVG.js → chunk-FWEO3HSL.js} +4 -4
- package/dist/chunk-H543GD6T.cjs +1696 -0
- package/dist/chunk-H543GD6T.cjs.map +1 -0
- package/dist/{chunk-ROXFP2LQ.cjs → chunk-HNDSPZ5T.cjs} +4 -4
- package/dist/{chunk-ROXFP2LQ.cjs.map → chunk-HNDSPZ5T.cjs.map} +1 -1
- package/dist/{chunk-MXF4DP2C.cjs → chunk-J3B45HTA.cjs} +74 -74
- package/dist/{chunk-MXF4DP2C.cjs.map → chunk-J3B45HTA.cjs.map} +1 -1
- package/dist/{chunk-GNO5PEMH.js → chunk-OK7T2LHK.js} +2 -2
- package/dist/chunk-P5TPUNBZ.js +1696 -0
- package/dist/chunk-P5TPUNBZ.js.map +1 -0
- package/dist/chunk-PDCITYBT.cjs +416 -0
- package/dist/chunk-PDCITYBT.cjs.map +1 -0
- package/dist/{chunk-RKJDBXTY.cjs → chunk-T4XWVPJK.cjs} +3 -3
- package/dist/{chunk-RKJDBXTY.cjs.map → chunk-T4XWVPJK.cjs.map} +1 -1
- package/dist/{chunk-ZQNDSUZO.js → chunk-UKI64KUD.js} +6 -6
- package/dist/{chunk-SVW2UNUC.js → chunk-WON34HKM.js} +2 -2
- package/dist/chunk-YLB2WVNA.js +416 -0
- package/dist/chunk-YLB2WVNA.js.map +1 -0
- package/dist/chunk-ZOOEX3WT.cjs +604 -0
- package/dist/chunk-ZOOEX3WT.cjs.map +1 -0
- package/dist/extensions/index.cjs +6 -6
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +5 -5
- package/dist/image-editor/index.cjs +4387 -690
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +1677 -590
- package/dist/image-editor/index.d.ts +1677 -590
- package/dist/image-editor/index.js +4273 -576
- package/dist/image-editor/index.js.map +1 -1
- package/dist/index.cjs +27 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -10
- package/dist/paint/index.cjs +38 -0
- package/dist/paint/index.cjs.map +1 -0
- package/dist/paint/index.d.cts +167 -0
- package/dist/paint/index.d.ts +167 -0
- package/dist/paint/index.js +38 -0
- package/dist/paint/index.js.map +1 -0
- package/dist/raster/index.cjs +121 -0
- package/dist/raster/index.cjs.map +1 -0
- package/dist/raster/index.d.cts +365 -0
- package/dist/raster/index.d.ts +365 -0
- package/dist/raster/index.js +121 -0
- package/dist/raster/index.js.map +1 -0
- package/dist/serializers/index.cjs +4 -4
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.js +3 -3
- package/dist/surface-fJhkmNQn.d.cts +181 -0
- package/dist/surface-fJhkmNQn.d.ts +181 -0
- package/dist/video-editor/index.cjs +930 -503
- package/dist/video-editor/index.cjs.map +1 -1
- package/dist/video-editor/index.d.cts +158 -11
- package/dist/video-editor/index.d.ts +158 -11
- package/dist/video-editor/index.js +841 -414
- package/dist/video-editor/index.js.map +1 -1
- package/package.json +13 -2
- package/skills/pixerate-image-editor/SKILL.md +111 -11
- package/skills/pixerate-video-editor/SKILL.md +11 -2
- /package/dist/{chunk-IMSX4IVG.js.map → chunk-FWEO3HSL.js.map} +0 -0
- /package/dist/{chunk-GNO5PEMH.js.map → chunk-OK7T2LHK.js.map} +0 -0
- /package/dist/{chunk-ZQNDSUZO.js.map → chunk-UKI64KUD.js.map} +0 -0
- /package/dist/{chunk-SVW2UNUC.js.map → chunk-WON34HKM.js.map} +0 -0
|
@@ -0,0 +1,1696 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TILE,
|
|
3
|
+
blendChannel,
|
|
4
|
+
blendRgb,
|
|
5
|
+
emptySurface,
|
|
6
|
+
isSeparableBlendMode,
|
|
7
|
+
readRegion,
|
|
8
|
+
surfaceFromPixels,
|
|
9
|
+
tileKey,
|
|
10
|
+
tilesInRect
|
|
11
|
+
} from "./chunk-D4ZCU72J.js";
|
|
12
|
+
|
|
13
|
+
// src/raster/blur.ts
|
|
14
|
+
function gaussianKernel(sigma) {
|
|
15
|
+
const r = Math.ceil(sigma * 3);
|
|
16
|
+
const k = new Float32Array(2 * r + 1);
|
|
17
|
+
let s = 0;
|
|
18
|
+
for (let i = -r; i <= r; i++) {
|
|
19
|
+
const v = Math.exp(-(i * i) / (2 * sigma * sigma));
|
|
20
|
+
k[i + r] = v;
|
|
21
|
+
s += v;
|
|
22
|
+
}
|
|
23
|
+
for (let i = 0; i < k.length; i++) k[i] /= s;
|
|
24
|
+
return k;
|
|
25
|
+
}
|
|
26
|
+
var DIRECT_GAUSSIAN_MAX_SIGMA = 8;
|
|
27
|
+
function boxRadii(sigma) {
|
|
28
|
+
const n = 3;
|
|
29
|
+
const wIdeal = Math.sqrt(12 * sigma * sigma / n + 1);
|
|
30
|
+
let wl = Math.floor(wIdeal);
|
|
31
|
+
if (wl % 2 === 0) wl--;
|
|
32
|
+
const wu = wl + 2;
|
|
33
|
+
const mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
|
|
34
|
+
const m = Math.round(mIdeal);
|
|
35
|
+
const sizes = [0, 1, 2].map((i) => i < m ? wl : wu);
|
|
36
|
+
return sizes.map((s) => (s - 1) / 2);
|
|
37
|
+
}
|
|
38
|
+
function convolveLine(src, off, stride, len, k, edge, dst) {
|
|
39
|
+
const r = (k.length - 1) / 2;
|
|
40
|
+
for (let i = 0; i < len; i++) {
|
|
41
|
+
let a = 0;
|
|
42
|
+
const lo = i - r;
|
|
43
|
+
for (let j = 0; j < k.length; j++) {
|
|
44
|
+
let x = lo + j;
|
|
45
|
+
if (x < 0 || x >= len) {
|
|
46
|
+
if (edge === "zero") continue;
|
|
47
|
+
x = x < 0 ? 0 : len - 1;
|
|
48
|
+
}
|
|
49
|
+
a += src[off + x * stride] * k[j];
|
|
50
|
+
}
|
|
51
|
+
dst[i] = a;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function boxesLine(src, off, stride, len, radii, edge, a, b, dst) {
|
|
55
|
+
const pad = radii.reduce((s, r) => s + r, 0);
|
|
56
|
+
const plen = len + 2 * pad;
|
|
57
|
+
for (let i = 0; i < plen; i++) {
|
|
58
|
+
const j = i - pad;
|
|
59
|
+
a[i] = j < 0 ? edge === "zero" ? 0 : src[off] : j >= len ? edge === "zero" ? 0 : src[off + (len - 1) * stride] : src[off + j * stride];
|
|
60
|
+
}
|
|
61
|
+
let from = a;
|
|
62
|
+
let to = b;
|
|
63
|
+
for (const r of radii) {
|
|
64
|
+
let sum = 0;
|
|
65
|
+
for (let i = 0; i <= Math.min(r, plen - 1); i++) sum += from[i];
|
|
66
|
+
const k = 2 * r + 1;
|
|
67
|
+
for (let i = 0; i < plen; i++) {
|
|
68
|
+
to[i] = sum / k;
|
|
69
|
+
const add = i + r + 1;
|
|
70
|
+
const drop = i - r;
|
|
71
|
+
if (add < plen) sum += from[add];
|
|
72
|
+
if (drop >= 0) sum -= from[drop];
|
|
73
|
+
}
|
|
74
|
+
const t = from;
|
|
75
|
+
from = to;
|
|
76
|
+
to = t;
|
|
77
|
+
}
|
|
78
|
+
for (let i = 0; i < len; i++) dst[i] = from[i + pad];
|
|
79
|
+
}
|
|
80
|
+
function gaussianBlur(src, w, h, sigma, edge = "zero") {
|
|
81
|
+
const out = Float32Array.from(src);
|
|
82
|
+
if (!(sigma >= 0.1) || w <= 0 || h <= 0) return out;
|
|
83
|
+
const s = Math.min(sigma, 4 * Math.max(w, h));
|
|
84
|
+
const line = new Float32Array(Math.max(w, h));
|
|
85
|
+
const passes = [];
|
|
86
|
+
if (s <= DIRECT_GAUSSIAN_MAX_SIGMA) {
|
|
87
|
+
const k = gaussianKernel(s);
|
|
88
|
+
passes.push((buf, off, stride, len) => convolveLine(buf, off, stride, len, k, edge, line));
|
|
89
|
+
} else {
|
|
90
|
+
const radii = boxRadii(s);
|
|
91
|
+
const plen = Math.max(w, h) + 2 * radii.reduce((a2, r) => a2 + r, 0);
|
|
92
|
+
const a = new Float32Array(plen);
|
|
93
|
+
const b = new Float32Array(plen);
|
|
94
|
+
passes.push((buf, off, stride, len) => boxesLine(buf, off, stride, len, radii, edge, a, b, line));
|
|
95
|
+
}
|
|
96
|
+
for (const pass of passes) {
|
|
97
|
+
for (let y = 0; y < h; y++) {
|
|
98
|
+
pass(out, y * w, 1, w);
|
|
99
|
+
out.set(line.subarray(0, w), y * w);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const pass of passes) {
|
|
103
|
+
for (let x = 0; x < w; x++) {
|
|
104
|
+
pass(out, x, w, h);
|
|
105
|
+
for (let y = 0; y < h; y++) out[y * w + x] = line[y];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/raster/layers.ts
|
|
112
|
+
var childrenOf = (l) => l.type === "group" ? l.children ?? null : null;
|
|
113
|
+
function flattenLayers(layers) {
|
|
114
|
+
const out = [];
|
|
115
|
+
const walk = (list) => {
|
|
116
|
+
for (const l of list) {
|
|
117
|
+
out.push(l);
|
|
118
|
+
const c = childrenOf(l);
|
|
119
|
+
if (c) walk(c);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
walk(layers);
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
function findLayer(layers, id) {
|
|
126
|
+
const walk = (list, parent, path) => {
|
|
127
|
+
for (let i = 0; i < list.length; i++) {
|
|
128
|
+
const l = list[i];
|
|
129
|
+
if (l.id === id) return { layer: l, parent, index: i, path };
|
|
130
|
+
const c = childrenOf(l);
|
|
131
|
+
if (c) {
|
|
132
|
+
const g = l;
|
|
133
|
+
const hit = walk(c, g, [...path, g]);
|
|
134
|
+
if (hit) return hit;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
};
|
|
139
|
+
return walk(layers, null, []);
|
|
140
|
+
}
|
|
141
|
+
function mapLayer(layers, id, fn) {
|
|
142
|
+
let changed = false;
|
|
143
|
+
const walk = (list) => {
|
|
144
|
+
let out = null;
|
|
145
|
+
for (let i = 0; i < list.length; i++) {
|
|
146
|
+
const l = list[i];
|
|
147
|
+
let next = l;
|
|
148
|
+
if (l.id === id) {
|
|
149
|
+
next = fn(l);
|
|
150
|
+
changed = true;
|
|
151
|
+
} else {
|
|
152
|
+
const c = childrenOf(l);
|
|
153
|
+
if (c) {
|
|
154
|
+
const children = walk(c);
|
|
155
|
+
if (children !== c) next = { ...l, children };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (next !== l) {
|
|
159
|
+
if (!out) out = list.slice(0, i);
|
|
160
|
+
if (next) out.push(next);
|
|
161
|
+
} else if (out) out.push(l);
|
|
162
|
+
}
|
|
163
|
+
return out ?? list;
|
|
164
|
+
};
|
|
165
|
+
const result = walk(layers);
|
|
166
|
+
return changed ? result : layers;
|
|
167
|
+
}
|
|
168
|
+
function insertLayer(layers, layer, parentId, index) {
|
|
169
|
+
const put = (list) => {
|
|
170
|
+
const at = index === void 0 ? list.length : Math.max(0, Math.min(list.length, index));
|
|
171
|
+
const next = list.slice();
|
|
172
|
+
next.splice(at, 0, layer);
|
|
173
|
+
return next;
|
|
174
|
+
};
|
|
175
|
+
if (!parentId) return put(layers);
|
|
176
|
+
return mapLayer(layers, parentId, (g) => {
|
|
177
|
+
const c = childrenOf(g);
|
|
178
|
+
return c ? { ...g, children: put(c) } : g;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function isLayerLocked(layer, what) {
|
|
182
|
+
return Boolean(layer.locks?.all || layer.locks?.[what]);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/raster/compositor.ts
|
|
186
|
+
var TILE_PIXELS = TILE * TILE;
|
|
187
|
+
var TILE_BYTES = TILE_PIXELS * 4;
|
|
188
|
+
function sameSig(a, b) {
|
|
189
|
+
if (!a || a.key !== b.key || a.refs.length !== b.refs.length) return false;
|
|
190
|
+
for (let i = 0; i < a.refs.length; i++) if (a.refs[i] !== b.refs[i]) return false;
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
function dirtyInTile(rects, tx, ty) {
|
|
194
|
+
let x0 = Infinity;
|
|
195
|
+
let y0 = Infinity;
|
|
196
|
+
let x1 = -Infinity;
|
|
197
|
+
let y1 = -Infinity;
|
|
198
|
+
const ox = tx * TILE;
|
|
199
|
+
const oy = ty * TILE;
|
|
200
|
+
for (const r of rects) {
|
|
201
|
+
const a = Math.max(ox, Math.floor(r.x));
|
|
202
|
+
const b = Math.max(oy, Math.floor(r.y));
|
|
203
|
+
const c = Math.min(ox + TILE, Math.ceil(r.x + r.width));
|
|
204
|
+
const d = Math.min(oy + TILE, Math.ceil(r.y + r.height));
|
|
205
|
+
if (c <= a || d <= b) continue;
|
|
206
|
+
x0 = Math.min(x0, a);
|
|
207
|
+
y0 = Math.min(y0, b);
|
|
208
|
+
x1 = Math.max(x1, c);
|
|
209
|
+
y1 = Math.max(y1, d);
|
|
210
|
+
}
|
|
211
|
+
return x0 === Infinity ? null : { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
212
|
+
}
|
|
213
|
+
function blendTile(dst, src, mode, alpha, coverage) {
|
|
214
|
+
if (!(alpha > 0)) return;
|
|
215
|
+
const normal = mode === "normal";
|
|
216
|
+
const lut = !normal && isSeparableBlendMode(mode) ? separableLut(mode) : null;
|
|
217
|
+
const cb = [0, 0, 0];
|
|
218
|
+
const cs = [0, 0, 0];
|
|
219
|
+
const n = dst.length;
|
|
220
|
+
for (let i = 0, p = 0; i < n; i += 4, p++) {
|
|
221
|
+
const sa = src[i + 3];
|
|
222
|
+
if (sa === 0) continue;
|
|
223
|
+
const k = coverage ? alpha * coverage[p] : alpha;
|
|
224
|
+
if (!(k > 0)) continue;
|
|
225
|
+
if (normal && sa === 255 && k >= 1) {
|
|
226
|
+
dst[i] = src[i];
|
|
227
|
+
dst[i + 1] = src[i + 1];
|
|
228
|
+
dst[i + 2] = src[i + 2];
|
|
229
|
+
dst[i + 3] = 255;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const as = sa / 255 * k;
|
|
233
|
+
const ab = dst[i + 3] / 255;
|
|
234
|
+
const ao = as + ab * (1 - as);
|
|
235
|
+
if (ao <= 0) {
|
|
236
|
+
dst[i] = dst[i + 1] = dst[i + 2] = dst[i + 3] = 0;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const d0 = dst[i];
|
|
240
|
+
const d1 = dst[i + 1];
|
|
241
|
+
const d2 = dst[i + 2];
|
|
242
|
+
const b0 = d0 / 255;
|
|
243
|
+
const b1 = d1 / 255;
|
|
244
|
+
const b2 = d2 / 255;
|
|
245
|
+
const s0 = src[i] / 255;
|
|
246
|
+
const s1 = src[i + 1] / 255;
|
|
247
|
+
const s2 = src[i + 2] / 255;
|
|
248
|
+
let m0;
|
|
249
|
+
let m1;
|
|
250
|
+
let m2;
|
|
251
|
+
if (normal) {
|
|
252
|
+
m0 = s0;
|
|
253
|
+
m1 = s1;
|
|
254
|
+
m2 = s2;
|
|
255
|
+
} else if (lut) {
|
|
256
|
+
m0 = lut[d0 << 8 | src[i]];
|
|
257
|
+
m1 = lut[d1 << 8 | src[i + 1]];
|
|
258
|
+
m2 = lut[d2 << 8 | src[i + 2]];
|
|
259
|
+
} else {
|
|
260
|
+
cb[0] = b0;
|
|
261
|
+
cb[1] = b1;
|
|
262
|
+
cb[2] = b2;
|
|
263
|
+
cs[0] = s0;
|
|
264
|
+
cs[1] = s1;
|
|
265
|
+
cs[2] = s2;
|
|
266
|
+
const m = blendRgb(mode, cb, cs);
|
|
267
|
+
m0 = m[0];
|
|
268
|
+
m1 = m[1];
|
|
269
|
+
m2 = m[2];
|
|
270
|
+
}
|
|
271
|
+
const wb = (1 - as) * ab;
|
|
272
|
+
const ws = (1 - ab) * as;
|
|
273
|
+
const wm = as * ab;
|
|
274
|
+
dst[i] = Math.round((wb * b0 + ws * s0 + wm * m0) / ao * 255);
|
|
275
|
+
dst[i + 1] = Math.round((wb * b1 + ws * s1 + wm * m1) / ao * 255);
|
|
276
|
+
dst[i + 2] = Math.round((wb * b2 + ws * s2 + wm * m2) / ao * 255);
|
|
277
|
+
dst[i + 3] = Math.round(ao * 255);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
var separableLuts = /* @__PURE__ */ new Map();
|
|
281
|
+
function separableLut(mode) {
|
|
282
|
+
let lut = separableLuts.get(mode);
|
|
283
|
+
if (!lut) {
|
|
284
|
+
lut = new Float64Array(65536);
|
|
285
|
+
for (let b = 0; b < 256; b++) for (let c = 0; c < 256; c++) lut[b << 8 | c] = blendChannel(mode, b / 255, c / 255);
|
|
286
|
+
separableLuts.set(mode, lut);
|
|
287
|
+
}
|
|
288
|
+
return lut;
|
|
289
|
+
}
|
|
290
|
+
function blendAtop(dst, src, mode, alpha, coverage) {
|
|
291
|
+
const keep = new Uint8ClampedArray(dst.length >> 2);
|
|
292
|
+
for (let i = 3, p = 0; i < dst.length; i += 4, p++) keep[p] = dst[i];
|
|
293
|
+
blendTile(dst, src, mode, alpha, coverage);
|
|
294
|
+
for (let i = 3, p = 0; i < dst.length; i += 4, p++) dst[i] = keep[p];
|
|
295
|
+
}
|
|
296
|
+
function lerpTile(dst, before, alpha, coverage) {
|
|
297
|
+
for (let i = 0, p = 0; i < dst.length; i += 4, p++) {
|
|
298
|
+
const k = coverage ? alpha * coverage[p] : alpha;
|
|
299
|
+
if (k >= 1) continue;
|
|
300
|
+
for (let c = 0; c < 4; c++) dst[i + c] = Math.round(before[i + c] + (dst[i + c] - before[i + c]) * k);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
var ZERO_TILE = new Uint8ClampedArray(TILE_BYTES);
|
|
304
|
+
var isGroup = (l) => l.type === "group";
|
|
305
|
+
var isPixel = (l) => l.type === "pixel";
|
|
306
|
+
var RasterCompositor = class {
|
|
307
|
+
constructor(profile = {}, options = {}) {
|
|
308
|
+
this.profile = profile;
|
|
309
|
+
this.maxCacheBytes = options.maxCacheBytes ?? 256 * 1024 * 1024;
|
|
310
|
+
}
|
|
311
|
+
profile;
|
|
312
|
+
tiles = /* @__PURE__ */ new Map();
|
|
313
|
+
maxCacheBytes;
|
|
314
|
+
cacheBytes = 0;
|
|
315
|
+
stamp = 0;
|
|
316
|
+
sources = { width: 1, height: 1 };
|
|
317
|
+
sourcesVersion = void 0;
|
|
318
|
+
filteredCache = /* @__PURE__ */ new WeakMap();
|
|
319
|
+
planeCache = /* @__PURE__ */ new Map();
|
|
320
|
+
planeIds = /* @__PURE__ */ new WeakMap();
|
|
321
|
+
nextPlaneId = 1;
|
|
322
|
+
contentCache = /* @__PURE__ */ new WeakMap();
|
|
323
|
+
stepsCache = /* @__PURE__ */ new WeakMap();
|
|
324
|
+
propsKeyCache = /* @__PURE__ */ new WeakMap();
|
|
325
|
+
lastStats = { tiles: 0, cached: 0, stepsRun: 0 };
|
|
326
|
+
hint = -1;
|
|
327
|
+
/** Drops every cached tile and plane. */
|
|
328
|
+
clear() {
|
|
329
|
+
this.tiles.clear();
|
|
330
|
+
this.cacheBytes = 0;
|
|
331
|
+
this.planeCache.clear();
|
|
332
|
+
this.filteredCache = /* @__PURE__ */ new WeakMap();
|
|
333
|
+
this.contentCache = /* @__PURE__ */ new WeakMap();
|
|
334
|
+
}
|
|
335
|
+
setSources(sources) {
|
|
336
|
+
const version = `${sources.width}x${sources.height}:${sources.version ?? ""}`;
|
|
337
|
+
if (version !== this.sourcesVersion) {
|
|
338
|
+
this.clear();
|
|
339
|
+
this.sourcesVersion = version;
|
|
340
|
+
}
|
|
341
|
+
this.sources = sources;
|
|
342
|
+
}
|
|
343
|
+
/** The composite of `layers` over `rect` as `rect.width × rect.height` RGBA (straight alpha). */
|
|
344
|
+
composite(layers, rect, sources, options = {}) {
|
|
345
|
+
const out = new Uint8ClampedArray(Math.max(0, rect.width * rect.height * 4));
|
|
346
|
+
this.compositeTiles(layers, rect, sources, (tx, ty, data) => {
|
|
347
|
+
const ox = tx * TILE;
|
|
348
|
+
const oy = ty * TILE;
|
|
349
|
+
const x0 = Math.max(rect.x, ox);
|
|
350
|
+
const y0 = Math.max(rect.y, oy);
|
|
351
|
+
const x1 = Math.min(rect.x + rect.width, ox + TILE);
|
|
352
|
+
const y1 = Math.min(rect.y + rect.height, oy + TILE);
|
|
353
|
+
const span = (x1 - x0) * 4;
|
|
354
|
+
for (let y = y0; y < y1; y++) {
|
|
355
|
+
const s = ((y - oy) * TILE + (x0 - ox)) * 4;
|
|
356
|
+
out.set(data.subarray(s, s + span), ((y - rect.y) * rect.width + (x0 - rect.x)) * 4);
|
|
357
|
+
}
|
|
358
|
+
}, options);
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Composites every tile intersecting `rect` and calls `onTile` with each tile's RGBA buffer
|
|
363
|
+
* (TILE × TILE). A tile whose inputs did not change since the last call returns the same buffer
|
|
364
|
+
* object; never mutate it.
|
|
365
|
+
*/
|
|
366
|
+
compositeTiles(layers, rect, sources, onTile, options = {}) {
|
|
367
|
+
this.setSources(sources);
|
|
368
|
+
const steps = this.buildSteps(layers);
|
|
369
|
+
this.hint = options.checkpointLayerId ? this.stepIndexOf(steps, options.checkpointLayerId) : -1;
|
|
370
|
+
this.stamp++;
|
|
371
|
+
const stats = { tiles: 0, cached: 0, stepsRun: 0 };
|
|
372
|
+
for (const { tx, ty } of tilesInRect(rect)) {
|
|
373
|
+
stats.tiles++;
|
|
374
|
+
const before = this.tiles.get(tileKey(tx, ty))?.result;
|
|
375
|
+
const data = this.compositeTile(layers, steps, tx, ty, stats, options.dirty ? dirtyInTile(options.dirty, tx, ty) : null);
|
|
376
|
+
onTile(tx, ty, data, data !== before);
|
|
377
|
+
}
|
|
378
|
+
this.lastStats = stats;
|
|
379
|
+
this.evict();
|
|
380
|
+
}
|
|
381
|
+
/** The composite of one tile (cached; do not mutate the result). */
|
|
382
|
+
tile(layers, tx, ty, sources) {
|
|
383
|
+
this.setSources(sources);
|
|
384
|
+
this.stamp++;
|
|
385
|
+
const stats = { tiles: 1, cached: 0, stepsRun: 0 };
|
|
386
|
+
this.hint = -1;
|
|
387
|
+
const data = this.compositeTile(layers, this.buildSteps(layers), tx, ty, stats);
|
|
388
|
+
this.lastStats = stats;
|
|
389
|
+
this.evict();
|
|
390
|
+
return data;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* A layer's effective mask coverage (0..1, after density, feather or parametric evaluation)
|
|
394
|
+
* over `r` in document space, or `null` when it reveals all of `r`.
|
|
395
|
+
*/
|
|
396
|
+
maskCoverage(layer, r, sources) {
|
|
397
|
+
this.setSources(sources);
|
|
398
|
+
return this.maskTile(layer, r);
|
|
399
|
+
}
|
|
400
|
+
// --- Steps -------------------------------------------------------------------
|
|
401
|
+
isFilter(l) {
|
|
402
|
+
return this.profile.isFilterLayer?.(l) ?? false;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Groups a layer list into compositing steps: clipped layers join the nearest unclipped
|
|
406
|
+
* content or group layer below; consecutive unclipped filter layers form one run.
|
|
407
|
+
*/
|
|
408
|
+
buildSteps(layers) {
|
|
409
|
+
const cached = this.stepsCache.get(layers);
|
|
410
|
+
if (cached) return cached;
|
|
411
|
+
const steps = [];
|
|
412
|
+
for (const layer of layers) {
|
|
413
|
+
const last = steps[steps.length - 1];
|
|
414
|
+
if (layer.clipped && last && (last.kind === "content" || last.kind === "group")) {
|
|
415
|
+
last.clipped.push(layer);
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (this.isFilter(layer)) {
|
|
419
|
+
if (last && last.kind === "adjust") last.layers.push(layer);
|
|
420
|
+
else steps.push({ kind: "adjust", layers: [layer] });
|
|
421
|
+
} else if (isGroup(layer)) {
|
|
422
|
+
steps.push({ kind: "group", layer, clipped: [], steps: this.buildSteps(layer.children ?? []) });
|
|
423
|
+
} else {
|
|
424
|
+
steps.push({ kind: "content", layer, clipped: [] });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
this.stepsCache.set(layers, steps);
|
|
428
|
+
return steps;
|
|
429
|
+
}
|
|
430
|
+
/** The index of the top-level step that holds layer `id` (searching groups and clipped layers), or -1. */
|
|
431
|
+
stepIndexOf(steps, id) {
|
|
432
|
+
const holds = (l) => l.id === id || isGroup(l) && (l.children ?? []).some(holds);
|
|
433
|
+
return steps.findIndex((s) => s.kind === "adjust" ? s.layers.some(holds) : holds(s.layer) || s.clipped.some(holds));
|
|
434
|
+
}
|
|
435
|
+
/** Properties of a layer that change pixels, minus its surfaces (memoised per object). */
|
|
436
|
+
propsKey(layer) {
|
|
437
|
+
let key = this.propsKeyCache.get(layer);
|
|
438
|
+
if (key !== void 0) return key;
|
|
439
|
+
const mask = layer.mask ? [layer.mask.enabled !== false, layer.mask.density ?? 1, layer.mask.feather ?? 0, layer.mask.defaultValue, layer.mask.source ?? null] : null;
|
|
440
|
+
const base = [layer.type, layer.id, layer.visible, layer.opacity, layer.blend, Boolean(layer.clipped), mask];
|
|
441
|
+
if (isPixel(layer)) base.push(layer.offset?.x ?? 0, layer.offset?.y ?? 0, layer.fillOpacity ?? 1);
|
|
442
|
+
const extra = this.profile.layerKey?.(layer);
|
|
443
|
+
if (extra !== void 0) base.push(extra);
|
|
444
|
+
key = JSON.stringify(base);
|
|
445
|
+
this.propsKeyCache.set(layer, key);
|
|
446
|
+
return key;
|
|
447
|
+
}
|
|
448
|
+
compositeTile(root, steps, tx, ty, stats, dirty = null) {
|
|
449
|
+
const key = tileKey(tx, ty);
|
|
450
|
+
const entry = this.tiles.get(key);
|
|
451
|
+
const sigs = steps.map((s) => this.stepSig(s, tx, ty));
|
|
452
|
+
let first = 0;
|
|
453
|
+
if (entry) {
|
|
454
|
+
while (first < sigs.length && first < entry.sigs.length && sameSig(entry.sigs[first], sigs[first])) first++;
|
|
455
|
+
if (first === sigs.length && first === entry.sigs.length) {
|
|
456
|
+
entry.stamp = this.stamp;
|
|
457
|
+
stats.cached++;
|
|
458
|
+
return entry.result;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
let start = 0;
|
|
462
|
+
let base = null;
|
|
463
|
+
if (entry) {
|
|
464
|
+
for (const [index, cp] of entry.checkpoints) {
|
|
465
|
+
if (index <= first && index > start) {
|
|
466
|
+
start = index;
|
|
467
|
+
base = cp;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const tileRect = { x: tx * TILE, y: ty * TILE, width: TILE, height: TILE };
|
|
472
|
+
const hint = this.hint;
|
|
473
|
+
const checkpoints = /* @__PURE__ */ new Map();
|
|
474
|
+
if (entry) {
|
|
475
|
+
for (const [index, cp] of entry.checkpoints) if (index <= start) checkpoints.set(index, cp);
|
|
476
|
+
}
|
|
477
|
+
let result;
|
|
478
|
+
if (entry && dirty && (dirty.width < TILE || dirty.height < TILE)) {
|
|
479
|
+
const r = dirty;
|
|
480
|
+
const sub = (data) => {
|
|
481
|
+
const out = new Uint8ClampedArray(r.width * r.height * 4);
|
|
482
|
+
if (!data) return out;
|
|
483
|
+
for (let y = 0; y < r.height; y++) {
|
|
484
|
+
const s = ((r.y - tileRect.y + y) * TILE + (r.x - tileRect.x)) * 4;
|
|
485
|
+
out.set(data.subarray(s, s + r.width * 4), y * r.width * 4);
|
|
486
|
+
}
|
|
487
|
+
return out;
|
|
488
|
+
};
|
|
489
|
+
const acc = sub(base ? base.data : null);
|
|
490
|
+
let empty = base ? base.empty : true;
|
|
491
|
+
for (let i = start; i < steps.length; i++) {
|
|
492
|
+
empty = this.runStep(steps[i], acc, empty, r);
|
|
493
|
+
stats.stepsRun++;
|
|
494
|
+
}
|
|
495
|
+
result = new Uint8ClampedArray(entry.result);
|
|
496
|
+
for (let y = 0; y < r.height; y++) {
|
|
497
|
+
result.set(acc.subarray(y * r.width * 4, (y + 1) * r.width * 4), ((r.y - tileRect.y + y) * TILE + (r.x - tileRect.x)) * 4);
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
const acc = base ? new Uint8ClampedArray(base.data) : new Uint8ClampedArray(TILE_BYTES);
|
|
501
|
+
let empty = base ? base.empty : true;
|
|
502
|
+
for (let i = start; i < steps.length; i++) {
|
|
503
|
+
if ((i === first || i === hint) && i > 0 && !checkpoints.has(i)) checkpoints.set(i, { data: new Uint8ClampedArray(acc), empty });
|
|
504
|
+
empty = this.runStep(steps[i], acc, empty, tileRect);
|
|
505
|
+
stats.stepsRun++;
|
|
506
|
+
}
|
|
507
|
+
result = empty ? ZERO_TILE : acc;
|
|
508
|
+
}
|
|
509
|
+
if (checkpoints.size > 2) {
|
|
510
|
+
const keys = [...checkpoints.keys()].sort((a, b) => a - b);
|
|
511
|
+
const keep = /* @__PURE__ */ new Set([keys[keys.length - 1], first, hint]);
|
|
512
|
+
for (const k of keys) if (!keep.has(k)) checkpoints.delete(k);
|
|
513
|
+
}
|
|
514
|
+
const prevBytes = entry ? this.entryBytes(entry) : 0;
|
|
515
|
+
const next = { sigs, checkpoints, result, root, stamp: this.stamp };
|
|
516
|
+
this.tiles.set(key, next);
|
|
517
|
+
this.cacheBytes += this.entryBytes(next) - prevBytes;
|
|
518
|
+
return result;
|
|
519
|
+
}
|
|
520
|
+
entryBytes(e) {
|
|
521
|
+
let n = e.result === ZERO_TILE ? 0 : TILE_BYTES;
|
|
522
|
+
for (const cp of e.checkpoints.values()) n += cp.data.byteLength;
|
|
523
|
+
return n;
|
|
524
|
+
}
|
|
525
|
+
evict() {
|
|
526
|
+
if (this.cacheBytes <= this.maxCacheBytes) return;
|
|
527
|
+
const entries = [...this.tiles.entries()].sort((a, b) => a[1].stamp - b[1].stamp);
|
|
528
|
+
for (const [, e] of entries) {
|
|
529
|
+
if (this.cacheBytes <= this.maxCacheBytes) return;
|
|
530
|
+
for (const cp of e.checkpoints.values()) this.cacheBytes -= cp.data.byteLength;
|
|
531
|
+
e.checkpoints.clear();
|
|
532
|
+
}
|
|
533
|
+
for (const [k, e] of entries) {
|
|
534
|
+
if (this.cacheBytes <= this.maxCacheBytes) return;
|
|
535
|
+
if (e.stamp === this.stamp) continue;
|
|
536
|
+
this.cacheBytes -= this.entryBytes(e);
|
|
537
|
+
this.tiles.delete(k);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// --- Signatures -----------------------------------------------------------
|
|
541
|
+
stepSig(step, tx, ty) {
|
|
542
|
+
if (step.kind === "adjust") {
|
|
543
|
+
const refs2 = [];
|
|
544
|
+
let key2 = "A";
|
|
545
|
+
for (const l of step.layers) {
|
|
546
|
+
key2 += this.propsKey(l);
|
|
547
|
+
refs2.push(...this.maskRefs(l, tx, ty, 0, 0));
|
|
548
|
+
}
|
|
549
|
+
return { key: key2, refs: refs2 };
|
|
550
|
+
}
|
|
551
|
+
const own = step.kind === "content" ? this.contentSig(step.layer, tx, ty) : this.groupSig(step.layer, step.steps, tx, ty);
|
|
552
|
+
if (!step.clipped.length) return own;
|
|
553
|
+
const refs = [...own.refs];
|
|
554
|
+
let key = own.key;
|
|
555
|
+
for (const c of step.clipped) {
|
|
556
|
+
const s = this.layerSig(c, tx, ty);
|
|
557
|
+
key += "|" + s.key;
|
|
558
|
+
refs.push(...s.refs);
|
|
559
|
+
}
|
|
560
|
+
return { key, refs };
|
|
561
|
+
}
|
|
562
|
+
layerSig(layer, tx, ty) {
|
|
563
|
+
if (this.isFilter(layer)) return { key: this.propsKey(layer), refs: this.maskRefs(layer, tx, ty, 0, 0) };
|
|
564
|
+
if (isGroup(layer)) return this.groupSig(layer, this.buildSteps(layer.children ?? []), tx, ty);
|
|
565
|
+
return this.contentSig(layer, tx, ty);
|
|
566
|
+
}
|
|
567
|
+
groupSig(group, steps, tx, ty) {
|
|
568
|
+
let key = "G" + this.propsKey(group);
|
|
569
|
+
const refs = this.maskRefs(group, tx, ty, 0, 0);
|
|
570
|
+
if (!group.visible) return { key, refs };
|
|
571
|
+
for (const s of steps) {
|
|
572
|
+
const sig = this.stepSig(s, tx, ty);
|
|
573
|
+
key += "[" + sig.key + "]";
|
|
574
|
+
refs.push(...sig.refs);
|
|
575
|
+
}
|
|
576
|
+
return { key, refs };
|
|
577
|
+
}
|
|
578
|
+
contentSig(layer, tx, ty) {
|
|
579
|
+
const key = "C" + this.propsKey(layer);
|
|
580
|
+
if (!layer.visible) return { key, refs: [] };
|
|
581
|
+
if (!isPixel(layer)) return { key, refs: this.maskRefs(layer, tx, ty, 0, 0) };
|
|
582
|
+
const { surface, dx, dy } = this.layerPixels(layer);
|
|
583
|
+
const refs = [surface.defaultValue];
|
|
584
|
+
const r = { x: tx * TILE - dx, y: ty * TILE - dy, width: TILE, height: TILE };
|
|
585
|
+
for (const t of tilesInRect(r)) refs.push(surface.getTile(t.tx, t.ty) ?? null);
|
|
586
|
+
refs.push(...this.maskRefs(layer, tx, ty, layer.offset?.x ?? 0, layer.offset?.y ?? 0));
|
|
587
|
+
return { key, refs };
|
|
588
|
+
}
|
|
589
|
+
maskRefs(layer, tx, ty, ox, oy) {
|
|
590
|
+
const mask = layer.mask;
|
|
591
|
+
if (!mask || mask.enabled === false) return [];
|
|
592
|
+
if (mask.source || (mask.feather ?? 0) > 0) return [this.planeFor(mask, ox, oy)];
|
|
593
|
+
const refs = [];
|
|
594
|
+
for (const t of tilesInRect({ x: tx * TILE - ox, y: ty * TILE - oy, width: TILE, height: TILE })) {
|
|
595
|
+
refs.push(mask.surface.getTile(t.tx, t.ty) ?? null);
|
|
596
|
+
}
|
|
597
|
+
return refs;
|
|
598
|
+
}
|
|
599
|
+
// --- Layer pixels ------------------------------------------------------------
|
|
600
|
+
/**
|
|
601
|
+
* A pixel layer's pixels after spatial filters: the surface and the offset to apply. Layers
|
|
602
|
+
* with a spatial filter are filtered once over the canvas (in document space, offset 0).
|
|
603
|
+
*/
|
|
604
|
+
layerPixels(layer) {
|
|
605
|
+
const raw = this.sources.surfaceOf ? this.sources.surfaceOf(layer) : layer.surface;
|
|
606
|
+
const ox = layer.offset?.x ?? 0;
|
|
607
|
+
const oy = layer.offset?.y ?? 0;
|
|
608
|
+
const filters = this.profile.pixelFilters?.(layer) ?? null;
|
|
609
|
+
if (!filters?.spatial) return { surface: raw, dx: ox, dy: oy, perPixel: filters };
|
|
610
|
+
const fkey = `${ox},${oy}:${filters.key}`;
|
|
611
|
+
let byKey = this.filteredCache.get(raw);
|
|
612
|
+
if (!byKey) {
|
|
613
|
+
byKey = /* @__PURE__ */ new Map();
|
|
614
|
+
this.filteredCache.set(raw, byKey);
|
|
615
|
+
}
|
|
616
|
+
let filtered = byKey.get(fkey);
|
|
617
|
+
if (!filtered) {
|
|
618
|
+
filtered = filterOverCanvas(raw, ox, oy, filters, this.sources.width, this.sources.height);
|
|
619
|
+
byKey.clear();
|
|
620
|
+
byKey.set(fkey, filtered);
|
|
621
|
+
}
|
|
622
|
+
return { surface: filtered, dx: 0, dy: 0, perPixel: null };
|
|
623
|
+
}
|
|
624
|
+
/** RGBA of a content layer over `r` (document space, inside one tile), or `null` when it has nothing there. */
|
|
625
|
+
contentTile(layer, r) {
|
|
626
|
+
if (!isPixel(layer)) return this.profile.contentTile?.(layer, r, this.sources) ?? null;
|
|
627
|
+
const { surface, dx, dy, perPixel } = this.layerPixels(layer);
|
|
628
|
+
const src = { x: r.x - dx, y: r.y - dy, width: r.width, height: r.height };
|
|
629
|
+
const covered = tilesInRect(src).map((t) => surface.getTile(t.tx, t.ty));
|
|
630
|
+
if (surface.defaultValue === 0 && covered.every((t) => !t)) return null;
|
|
631
|
+
const aligned = r.width === TILE && r.height === TILE && src.x % TILE === 0 && src.y % TILE === 0;
|
|
632
|
+
if (!perPixel) {
|
|
633
|
+
if (aligned && covered[0]) return covered[0].data;
|
|
634
|
+
return readRegion(surface, src);
|
|
635
|
+
}
|
|
636
|
+
const fkey = `${dx},${dy}:${perPixel.key}`;
|
|
637
|
+
const anchor = covered.find(Boolean) ?? surface;
|
|
638
|
+
let byKey = this.contentCache.get(anchor);
|
|
639
|
+
const refsKey = `${r.x},${r.y}:${fkey}`;
|
|
640
|
+
if (aligned) {
|
|
641
|
+
const hit = byKey?.get(refsKey);
|
|
642
|
+
if (hit) return hit;
|
|
643
|
+
}
|
|
644
|
+
const data = readRegion(surface, src);
|
|
645
|
+
perPixel.apply(data, r.width, r.height);
|
|
646
|
+
if (aligned) {
|
|
647
|
+
if (!byKey) {
|
|
648
|
+
byKey = /* @__PURE__ */ new Map();
|
|
649
|
+
this.contentCache.set(anchor, byKey);
|
|
650
|
+
}
|
|
651
|
+
byKey.set(refsKey, data);
|
|
652
|
+
}
|
|
653
|
+
return data;
|
|
654
|
+
}
|
|
655
|
+
// --- Masks -------------------------------------------------------------------
|
|
656
|
+
planeKey(mask, ox, oy) {
|
|
657
|
+
const W = this.sources.width;
|
|
658
|
+
const H = this.sources.height;
|
|
659
|
+
let id = this.planeIds.get(mask.surface);
|
|
660
|
+
if (id === void 0) {
|
|
661
|
+
id = this.nextPlaneId++;
|
|
662
|
+
this.planeIds.set(mask.surface, id);
|
|
663
|
+
}
|
|
664
|
+
return `${id}:${ox},${oy}:${W}x${H}:${mask.feather ?? 0}:${mask.density ?? 1}:${mask.source ? JSON.stringify(mask.source) : ""}`;
|
|
665
|
+
}
|
|
666
|
+
/** Canvas-sized coverage of a feathered or parametric mask (cached). */
|
|
667
|
+
planeFor(mask, ox, oy) {
|
|
668
|
+
const key = this.planeKey(mask, ox, oy);
|
|
669
|
+
let plane = this.planeCache.get(key);
|
|
670
|
+
if (plane) {
|
|
671
|
+
this.planeCache.delete(key);
|
|
672
|
+
this.planeCache.set(key, plane);
|
|
673
|
+
return plane;
|
|
674
|
+
}
|
|
675
|
+
const W = this.sources.width;
|
|
676
|
+
const H = this.sources.height;
|
|
677
|
+
let v = new Float32Array(W * H);
|
|
678
|
+
if (mask.source) {
|
|
679
|
+
const generated = this.profile.maskSourcePlane?.(mask.source, W, H, this.sources) ?? null;
|
|
680
|
+
if (!generated || generated.length !== W * H) v.fill(1);
|
|
681
|
+
else v.set(generated);
|
|
682
|
+
} else {
|
|
683
|
+
const bytes = readRegion(mask.surface, { x: -ox, y: -oy, width: W, height: H });
|
|
684
|
+
for (let i = 0; i < v.length; i++) v[i] = bytes[i] / 255;
|
|
685
|
+
}
|
|
686
|
+
const feather = mask.feather ?? 0;
|
|
687
|
+
if (feather > 0) v = gaussianBlur(v, W, H, feather, "clamp");
|
|
688
|
+
const density = mask.density ?? 1;
|
|
689
|
+
if (density !== 1) for (let i = 0; i < v.length; i++) v[i] = 1 - density * (1 - v[i]);
|
|
690
|
+
plane = { data: v, width: W, height: H };
|
|
691
|
+
this.planeCache.set(key, plane);
|
|
692
|
+
while (this.planeCache.size > 8) this.planeCache.delete(this.planeCache.keys().next().value);
|
|
693
|
+
return plane;
|
|
694
|
+
}
|
|
695
|
+
maskTile(layer, r) {
|
|
696
|
+
const mask = layer.mask;
|
|
697
|
+
if (!mask || mask.enabled === false) return null;
|
|
698
|
+
const ox = isPixel(layer) ? layer.offset?.x ?? 0 : 0;
|
|
699
|
+
const oy = isPixel(layer) ? layer.offset?.y ?? 0 : 0;
|
|
700
|
+
const n = r.width * r.height;
|
|
701
|
+
const out = new Float32Array(n);
|
|
702
|
+
const density = mask.density ?? 1;
|
|
703
|
+
if (mask.source || (mask.feather ?? 0) > 0) {
|
|
704
|
+
const plane = this.planeFor(mask, ox, oy);
|
|
705
|
+
const W = plane.width;
|
|
706
|
+
const H = plane.height;
|
|
707
|
+
const outside = mask.source ? 1 : 1 - density * (1 - mask.defaultValue / 255);
|
|
708
|
+
out.fill(outside);
|
|
709
|
+
const x0 = Math.max(0, r.x);
|
|
710
|
+
const y0 = Math.max(0, r.y);
|
|
711
|
+
const x1 = Math.min(W, r.x + r.width);
|
|
712
|
+
const y1 = Math.min(H, r.y + r.height);
|
|
713
|
+
for (let y = y0; y < y1; y++) {
|
|
714
|
+
const s = y * W;
|
|
715
|
+
const d = (y - r.y) * r.width - r.x;
|
|
716
|
+
for (let x = x0; x < x1; x++) out[d + x] = plane.data[s + x];
|
|
717
|
+
}
|
|
718
|
+
} else {
|
|
719
|
+
const bytes = readRegion(mask.surface, { x: r.x - ox, y: r.y - oy, width: r.width, height: r.height });
|
|
720
|
+
let all = true;
|
|
721
|
+
for (let i = 0; i < n; i++) {
|
|
722
|
+
out[i] = bytes[i] / 255;
|
|
723
|
+
if (bytes[i] !== 255) all = false;
|
|
724
|
+
}
|
|
725
|
+
if (all) return null;
|
|
726
|
+
if (density !== 1) for (let i = 0; i < n; i++) out[i] = 1 - density * (1 - out[i]);
|
|
727
|
+
}
|
|
728
|
+
return out;
|
|
729
|
+
}
|
|
730
|
+
// --- Running steps ---------------------------------------------------------
|
|
731
|
+
/** Runs one step onto `acc`; returns whether `acc` is still empty. */
|
|
732
|
+
runStep(step, acc, empty, r) {
|
|
733
|
+
if (step.kind === "adjust") {
|
|
734
|
+
if (empty) return true;
|
|
735
|
+
this.applyFilterRun(step.layers, acc, r);
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
const layer = step.layer;
|
|
739
|
+
if (!layer.visible) return empty;
|
|
740
|
+
const clipped = step.clipped.filter((l) => l.visible);
|
|
741
|
+
const coverage = this.maskTile(layer, r);
|
|
742
|
+
if (coverage && coverage.every((v) => v <= 0)) return empty;
|
|
743
|
+
if (step.kind === "group") {
|
|
744
|
+
if (layer.blend === "pass-through" && !clipped.length) {
|
|
745
|
+
if (layer.opacity >= 1 && !coverage) return this.runSteps(step.steps, acc, empty, r);
|
|
746
|
+
const before = new Uint8ClampedArray(acc);
|
|
747
|
+
const stillEmpty = this.runSteps(step.steps, acc, empty, r);
|
|
748
|
+
lerpTile(acc, before, layer.opacity, coverage);
|
|
749
|
+
return empty && stillEmpty;
|
|
750
|
+
}
|
|
751
|
+
const buf2 = new Uint8ClampedArray(acc.length);
|
|
752
|
+
if (this.runSteps(step.steps, buf2, true, r)) return empty;
|
|
753
|
+
this.applyClipped(clipped, buf2, r);
|
|
754
|
+
const mode = layer.blend === "pass-through" ? "normal" : layer.blend;
|
|
755
|
+
return this.blendOnto(acc, buf2, empty, mode, layer.opacity, coverage);
|
|
756
|
+
}
|
|
757
|
+
const content = this.contentTile(step.layer, r);
|
|
758
|
+
if (!content) return empty;
|
|
759
|
+
const fill = isPixel(step.layer) ? step.layer.fillOpacity ?? 1 : 1;
|
|
760
|
+
const blend = this.contentBlend(step.layer);
|
|
761
|
+
if (!clipped.length) return this.blendOnto(acc, content, empty, blend, layer.opacity * fill, coverage);
|
|
762
|
+
const buf = new Uint8ClampedArray(acc.length);
|
|
763
|
+
blendTile(buf, content, "normal", fill, coverage);
|
|
764
|
+
this.applyClipped(clipped, buf, r);
|
|
765
|
+
return this.blendOnto(acc, buf, empty, blend, layer.opacity, null);
|
|
766
|
+
}
|
|
767
|
+
runSteps(steps, acc, empty, r) {
|
|
768
|
+
for (const s of steps) empty = this.runStep(s, acc, empty, r);
|
|
769
|
+
return empty;
|
|
770
|
+
}
|
|
771
|
+
contentBlend(layer) {
|
|
772
|
+
return this.profile.contentBlend?.(layer) ?? layer.blend;
|
|
773
|
+
}
|
|
774
|
+
blendOnto(acc, src, empty, mode, alpha, coverage) {
|
|
775
|
+
if (!(alpha > 0)) return empty;
|
|
776
|
+
if (empty && mode === "normal" && alpha >= 1 && !coverage) {
|
|
777
|
+
acc.set(src);
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
blendTile(acc, src, mode, alpha, coverage);
|
|
781
|
+
return false;
|
|
782
|
+
}
|
|
783
|
+
applyClipped(clipped, buf, r) {
|
|
784
|
+
let run = [];
|
|
785
|
+
const flush = () => {
|
|
786
|
+
if (run.length) this.applyFilterRun(run, buf, r);
|
|
787
|
+
run = [];
|
|
788
|
+
};
|
|
789
|
+
for (const c of clipped) {
|
|
790
|
+
if (this.isFilter(c)) {
|
|
791
|
+
run.push(c);
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
flush();
|
|
795
|
+
const coverage = this.maskTile(c, r);
|
|
796
|
+
if (isGroup(c)) {
|
|
797
|
+
const inner = new Uint8ClampedArray(buf.length);
|
|
798
|
+
if (this.runSteps(this.buildSteps(c.children ?? []), inner, true, r)) continue;
|
|
799
|
+
const mode = c.blend === "pass-through" ? "normal" : c.blend;
|
|
800
|
+
blendAtop(buf, inner, mode, c.opacity, coverage);
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
const content = this.contentTile(c, r);
|
|
804
|
+
if (!content) continue;
|
|
805
|
+
const fill = isPixel(c) ? c.fillOpacity ?? 1 : 1;
|
|
806
|
+
blendAtop(buf, content, this.contentBlend(c), c.opacity * fill, coverage);
|
|
807
|
+
}
|
|
808
|
+
flush();
|
|
809
|
+
}
|
|
810
|
+
applyFilterRun(layers, acc, r) {
|
|
811
|
+
const visible = [];
|
|
812
|
+
let masks;
|
|
813
|
+
for (const node of layers) {
|
|
814
|
+
if (!node.visible) continue;
|
|
815
|
+
const cov = this.maskTile(node, r);
|
|
816
|
+
if (cov) {
|
|
817
|
+
if (cov.every((v) => v <= 0)) continue;
|
|
818
|
+
(masks ??= /* @__PURE__ */ new Map()).set(node.id, cov);
|
|
819
|
+
}
|
|
820
|
+
visible.push(node);
|
|
821
|
+
}
|
|
822
|
+
if (visible.length) this.profile.applyFilterRun?.(visible, acc, r, masks, this.sources);
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
function filterOverCanvas(surface, ox, oy, filters, width, height) {
|
|
826
|
+
if (width <= 0 || height <= 0) return emptySurface(4);
|
|
827
|
+
const data = readRegion(surface, { x: -ox, y: -oy, width, height });
|
|
828
|
+
filters.apply(data, width, height);
|
|
829
|
+
return surfaceFromPixels({ width, height, data }, 4, 0, 0);
|
|
830
|
+
}
|
|
831
|
+
function changedTiles(a, b) {
|
|
832
|
+
const out = [];
|
|
833
|
+
for (const [k, t] of b.tiles) if (a.tiles.get(k) !== t) out.push(k);
|
|
834
|
+
for (const k of a.tiles.keys()) if (!b.tiles.has(k)) out.push(k);
|
|
835
|
+
return out;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/raster/zlib.ts
|
|
839
|
+
var LENGTH_BASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
|
|
840
|
+
var LENGTH_EXTRA = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
|
|
841
|
+
var DIST_BASE = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
|
|
842
|
+
var DIST_EXTRA = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
|
|
843
|
+
var CL_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
|
844
|
+
var ZlibError = class extends Error {
|
|
845
|
+
constructor(message) {
|
|
846
|
+
super(message);
|
|
847
|
+
this.name = "ZlibError";
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
function buildHuffman(lengths, n) {
|
|
851
|
+
const counts = new Uint16Array(16);
|
|
852
|
+
for (let i = 0; i < n; i++) counts[lengths[i]]++;
|
|
853
|
+
counts[0] = 0;
|
|
854
|
+
const offs = new Uint16Array(16);
|
|
855
|
+
for (let i = 1; i < 16; i++) offs[i] = offs[i - 1] + counts[i - 1];
|
|
856
|
+
const symbols = new Uint16Array(n);
|
|
857
|
+
for (let i = 0; i < n; i++) if (lengths[i]) symbols[offs[lengths[i]]++] = i;
|
|
858
|
+
return { counts, symbols };
|
|
859
|
+
}
|
|
860
|
+
var fixedLit = null;
|
|
861
|
+
var fixedDist = null;
|
|
862
|
+
function fixedTables() {
|
|
863
|
+
if (!fixedLit || !fixedDist) {
|
|
864
|
+
const l = new Uint8Array(288);
|
|
865
|
+
for (let i = 0; i < 144; i++) l[i] = 8;
|
|
866
|
+
for (let i = 144; i < 256; i++) l[i] = 9;
|
|
867
|
+
for (let i = 256; i < 280; i++) l[i] = 7;
|
|
868
|
+
for (let i = 280; i < 288; i++) l[i] = 8;
|
|
869
|
+
fixedLit = buildHuffman(l, 288);
|
|
870
|
+
const d = new Uint8Array(30).fill(5);
|
|
871
|
+
fixedDist = buildHuffman(d, 30);
|
|
872
|
+
}
|
|
873
|
+
return [fixedLit, fixedDist];
|
|
874
|
+
}
|
|
875
|
+
var BitReader = class {
|
|
876
|
+
constructor(data) {
|
|
877
|
+
this.data = data;
|
|
878
|
+
}
|
|
879
|
+
data;
|
|
880
|
+
pos = 0;
|
|
881
|
+
bit = 0;
|
|
882
|
+
bits = 0;
|
|
883
|
+
need(n) {
|
|
884
|
+
while (this.bits < n) {
|
|
885
|
+
if (this.pos >= this.data.length) throw new ZlibError("Unexpected end of compressed data");
|
|
886
|
+
this.bit |= this.data[this.pos++] << this.bits;
|
|
887
|
+
this.bits += 8;
|
|
888
|
+
}
|
|
889
|
+
const v = this.bit & (1 << n) - 1;
|
|
890
|
+
this.bit >>>= n;
|
|
891
|
+
this.bits -= n;
|
|
892
|
+
return v;
|
|
893
|
+
}
|
|
894
|
+
decode(h) {
|
|
895
|
+
let code = 0;
|
|
896
|
+
let first = 0;
|
|
897
|
+
let index = 0;
|
|
898
|
+
for (let len = 1; len < 16; len++) {
|
|
899
|
+
code |= this.need(1);
|
|
900
|
+
const count = h.counts[len];
|
|
901
|
+
if (code - count < first) return h.symbols[index + (code - first)];
|
|
902
|
+
index += count;
|
|
903
|
+
first += count;
|
|
904
|
+
first <<= 1;
|
|
905
|
+
code <<= 1;
|
|
906
|
+
}
|
|
907
|
+
throw new ZlibError("Invalid Huffman code");
|
|
908
|
+
}
|
|
909
|
+
align() {
|
|
910
|
+
this.bit = 0;
|
|
911
|
+
this.bits = 0;
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
var Output = class {
|
|
915
|
+
constructor(hint, max) {
|
|
916
|
+
this.max = max;
|
|
917
|
+
this.buf = new Uint8Array(Math.max(1024, Math.min(hint, max)));
|
|
918
|
+
}
|
|
919
|
+
max;
|
|
920
|
+
buf;
|
|
921
|
+
len = 0;
|
|
922
|
+
ensure(n) {
|
|
923
|
+
if (this.len + n > this.max) throw new ZlibError(`Decompressed data exceeds ${this.max} bytes`);
|
|
924
|
+
if (this.len + n <= this.buf.length) return;
|
|
925
|
+
let size = this.buf.length * 2;
|
|
926
|
+
while (size < this.len + n) size *= 2;
|
|
927
|
+
const next = new Uint8Array(Math.min(size, Math.max(this.max, this.len + n)));
|
|
928
|
+
next.set(this.buf.subarray(0, this.len));
|
|
929
|
+
this.buf = next;
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
function inflateRaw(data, options = {}) {
|
|
933
|
+
const max = options.maxOutput ?? 512 * 1024 * 1024;
|
|
934
|
+
const out = new Output(options.sizeHint ?? data.length * 4, max);
|
|
935
|
+
const br = new BitReader(data);
|
|
936
|
+
let final = 0;
|
|
937
|
+
while (!final) {
|
|
938
|
+
final = br.need(1);
|
|
939
|
+
const type = br.need(2);
|
|
940
|
+
if (type === 0) {
|
|
941
|
+
br.align();
|
|
942
|
+
if (br.pos + 4 > data.length) throw new ZlibError("Unexpected end of compressed data");
|
|
943
|
+
const len = data[br.pos] | data[br.pos + 1] << 8;
|
|
944
|
+
const nlen = data[br.pos + 2] | data[br.pos + 3] << 8;
|
|
945
|
+
br.pos += 4;
|
|
946
|
+
if ((len ^ 65535) !== nlen) throw new ZlibError("Stored block length mismatch");
|
|
947
|
+
if (br.pos + len > data.length) throw new ZlibError("Unexpected end of compressed data");
|
|
948
|
+
out.ensure(len);
|
|
949
|
+
out.buf.set(data.subarray(br.pos, br.pos + len), out.len);
|
|
950
|
+
out.len += len;
|
|
951
|
+
br.pos += len;
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
let lit;
|
|
955
|
+
let dist;
|
|
956
|
+
if (type === 1) [lit, dist] = fixedTables();
|
|
957
|
+
else if (type === 2) {
|
|
958
|
+
const hlit = br.need(5) + 257;
|
|
959
|
+
const hdist = br.need(5) + 1;
|
|
960
|
+
const hclen = br.need(4) + 4;
|
|
961
|
+
const cl = new Uint8Array(19);
|
|
962
|
+
for (let i2 = 0; i2 < hclen; i2++) cl[CL_ORDER[i2]] = br.need(3);
|
|
963
|
+
const clh = buildHuffman(cl, 19);
|
|
964
|
+
const lengths = new Uint8Array(hlit + hdist);
|
|
965
|
+
let i = 0;
|
|
966
|
+
while (i < hlit + hdist) {
|
|
967
|
+
const sym = br.decode(clh);
|
|
968
|
+
if (sym < 16) lengths[i++] = sym;
|
|
969
|
+
else {
|
|
970
|
+
let rep = 0;
|
|
971
|
+
let val = 0;
|
|
972
|
+
if (sym === 16) {
|
|
973
|
+
if (i === 0) throw new ZlibError("Repeat with no previous length");
|
|
974
|
+
val = lengths[i - 1];
|
|
975
|
+
rep = 3 + br.need(2);
|
|
976
|
+
} else if (sym === 17) rep = 3 + br.need(3);
|
|
977
|
+
else rep = 11 + br.need(7);
|
|
978
|
+
if (i + rep > hlit + hdist) throw new ZlibError("Too many code lengths");
|
|
979
|
+
while (rep--) lengths[i++] = val;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
lit = buildHuffman(lengths.subarray(0, hlit), hlit);
|
|
983
|
+
dist = buildHuffman(lengths.subarray(hlit), hdist);
|
|
984
|
+
} else throw new ZlibError("Invalid block type");
|
|
985
|
+
for (; ; ) {
|
|
986
|
+
const sym = br.decode(lit);
|
|
987
|
+
if (sym < 256) {
|
|
988
|
+
out.ensure(1);
|
|
989
|
+
out.buf[out.len++] = sym;
|
|
990
|
+
} else if (sym === 256) break;
|
|
991
|
+
else {
|
|
992
|
+
const li = sym - 257;
|
|
993
|
+
if (li >= 29) throw new ZlibError("Invalid length symbol");
|
|
994
|
+
const len = LENGTH_BASE[li] + br.need(LENGTH_EXTRA[li]);
|
|
995
|
+
const di = br.decode(dist);
|
|
996
|
+
if (di >= 30) throw new ZlibError("Invalid distance symbol");
|
|
997
|
+
const d = DIST_BASE[di] + br.need(DIST_EXTRA[di]);
|
|
998
|
+
if (d > out.len) throw new ZlibError("Distance too far back");
|
|
999
|
+
out.ensure(len);
|
|
1000
|
+
const buf = out.buf;
|
|
1001
|
+
let p = out.len;
|
|
1002
|
+
for (let k = 0; k < len; k++, p++) buf[p] = buf[p - d];
|
|
1003
|
+
out.len = p;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return out.buf.slice(0, out.len);
|
|
1008
|
+
}
|
|
1009
|
+
function adler32(data) {
|
|
1010
|
+
let a = 1;
|
|
1011
|
+
let b = 0;
|
|
1012
|
+
const n = data.length;
|
|
1013
|
+
let i = 0;
|
|
1014
|
+
while (i < n) {
|
|
1015
|
+
const end = Math.min(n, i + 3800);
|
|
1016
|
+
for (; i < end; i++) {
|
|
1017
|
+
a += data[i];
|
|
1018
|
+
b += a;
|
|
1019
|
+
}
|
|
1020
|
+
a %= 65521;
|
|
1021
|
+
b %= 65521;
|
|
1022
|
+
}
|
|
1023
|
+
return (b << 16 | a) >>> 0;
|
|
1024
|
+
}
|
|
1025
|
+
function inflate(data, options = {}) {
|
|
1026
|
+
if (data.length < 6) throw new ZlibError("Truncated zlib stream");
|
|
1027
|
+
const cmf = data[0];
|
|
1028
|
+
const flg = data[1];
|
|
1029
|
+
if ((cmf & 15) !== 8 || (cmf << 8 | flg) % 31 !== 0) throw new ZlibError("Not a zlib stream");
|
|
1030
|
+
if (flg & 32) throw new ZlibError("Preset dictionaries are not supported");
|
|
1031
|
+
const out = inflateRaw(data.subarray(2), options);
|
|
1032
|
+
if (options.verify !== false && data.length >= 6) {
|
|
1033
|
+
const t = data.length - 4;
|
|
1034
|
+
const want = (data[t] << 24 | data[t + 1] << 16 | data[t + 2] << 8 | data[t + 3]) >>> 0;
|
|
1035
|
+
if (adler32(out) !== want) throw new ZlibError("Adler-32 mismatch");
|
|
1036
|
+
}
|
|
1037
|
+
return out;
|
|
1038
|
+
}
|
|
1039
|
+
var BitWriter = class {
|
|
1040
|
+
buf;
|
|
1041
|
+
len = 0;
|
|
1042
|
+
bit = 0;
|
|
1043
|
+
bits = 0;
|
|
1044
|
+
constructor(hint) {
|
|
1045
|
+
this.buf = new Uint8Array(Math.max(1024, hint));
|
|
1046
|
+
}
|
|
1047
|
+
grow() {
|
|
1048
|
+
const next = new Uint8Array(this.buf.length * 2);
|
|
1049
|
+
next.set(this.buf);
|
|
1050
|
+
this.buf = next;
|
|
1051
|
+
}
|
|
1052
|
+
write(value, n) {
|
|
1053
|
+
this.bit |= value << this.bits;
|
|
1054
|
+
this.bits += n;
|
|
1055
|
+
while (this.bits >= 8) {
|
|
1056
|
+
if (this.len >= this.buf.length) this.grow();
|
|
1057
|
+
this.buf[this.len++] = this.bit & 255;
|
|
1058
|
+
this.bit >>>= 8;
|
|
1059
|
+
this.bits -= 8;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
flush() {
|
|
1063
|
+
if (this.bits > 0) {
|
|
1064
|
+
if (this.len >= this.buf.length) this.grow();
|
|
1065
|
+
this.buf[this.len++] = this.bit & 255;
|
|
1066
|
+
}
|
|
1067
|
+
this.bit = 0;
|
|
1068
|
+
this.bits = 0;
|
|
1069
|
+
}
|
|
1070
|
+
bytes() {
|
|
1071
|
+
return this.buf.slice(0, this.len);
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
function codeLengths(freqs, maxBits) {
|
|
1075
|
+
const n = freqs.length;
|
|
1076
|
+
const lengths = new Uint8Array(n);
|
|
1077
|
+
const f = Float64Array.from(freqs);
|
|
1078
|
+
for (; ; ) {
|
|
1079
|
+
const nodes = [];
|
|
1080
|
+
for (let i = 0; i < n; i++) if (f[i] > 0) nodes.push({ w: f[i], sym: i });
|
|
1081
|
+
lengths.fill(0);
|
|
1082
|
+
if (nodes.length === 0) return lengths;
|
|
1083
|
+
if (nodes.length === 1) {
|
|
1084
|
+
lengths[nodes[0].sym] = 1;
|
|
1085
|
+
return lengths;
|
|
1086
|
+
}
|
|
1087
|
+
const heap = nodes.map((_, i) => i);
|
|
1088
|
+
const less = (a, b) => nodes[a].w < nodes[b].w || nodes[a].w === nodes[b].w && a < b;
|
|
1089
|
+
const down = (i) => {
|
|
1090
|
+
for (; ; ) {
|
|
1091
|
+
const l = 2 * i + 1;
|
|
1092
|
+
const r = l + 1;
|
|
1093
|
+
let m = i;
|
|
1094
|
+
if (l < heap.length && less(heap[l], heap[m])) m = l;
|
|
1095
|
+
if (r < heap.length && less(heap[r], heap[m])) m = r;
|
|
1096
|
+
if (m === i) return;
|
|
1097
|
+
[heap[i], heap[m]] = [heap[m], heap[i]];
|
|
1098
|
+
i = m;
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
const up = (i) => {
|
|
1102
|
+
while (i > 0) {
|
|
1103
|
+
const p = i - 1 >> 1;
|
|
1104
|
+
if (!less(heap[i], heap[p])) return;
|
|
1105
|
+
[heap[i], heap[p]] = [heap[p], heap[i]];
|
|
1106
|
+
i = p;
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
for (let i = (heap.length >> 1) - 1; i >= 0; i--) down(i);
|
|
1110
|
+
const pop = () => {
|
|
1111
|
+
const top = heap[0];
|
|
1112
|
+
const last = heap.pop();
|
|
1113
|
+
if (heap.length) {
|
|
1114
|
+
heap[0] = last;
|
|
1115
|
+
down(0);
|
|
1116
|
+
}
|
|
1117
|
+
return top;
|
|
1118
|
+
};
|
|
1119
|
+
while (heap.length > 1) {
|
|
1120
|
+
const a = pop();
|
|
1121
|
+
const b = pop();
|
|
1122
|
+
nodes.push({ w: nodes[a].w + nodes[b].w, sym: -1, left: a, right: b });
|
|
1123
|
+
heap.push(nodes.length - 1);
|
|
1124
|
+
up(heap.length - 1);
|
|
1125
|
+
}
|
|
1126
|
+
let maxLen = 0;
|
|
1127
|
+
const stack = [[heap[0], 0]];
|
|
1128
|
+
while (stack.length) {
|
|
1129
|
+
const [i, d] = stack.pop();
|
|
1130
|
+
const node = nodes[i];
|
|
1131
|
+
if (node.sym >= 0) {
|
|
1132
|
+
lengths[node.sym] = d;
|
|
1133
|
+
if (d > maxLen) maxLen = d;
|
|
1134
|
+
} else {
|
|
1135
|
+
stack.push([node.left, d + 1], [node.right, d + 1]);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (maxLen <= maxBits) return lengths;
|
|
1139
|
+
for (let i = 0; i < n; i++) if (f[i] > 0) f[i] = Math.floor(f[i] / 2) + 1;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
function canonicalCodes(lengths) {
|
|
1143
|
+
const blCount = new Uint16Array(16);
|
|
1144
|
+
for (const l of lengths) blCount[l]++;
|
|
1145
|
+
blCount[0] = 0;
|
|
1146
|
+
const next = new Uint16Array(16);
|
|
1147
|
+
let code = 0;
|
|
1148
|
+
for (let bits = 1; bits < 16; bits++) {
|
|
1149
|
+
code = code + blCount[bits - 1] << 1;
|
|
1150
|
+
next[bits] = code;
|
|
1151
|
+
}
|
|
1152
|
+
const codes = new Uint16Array(lengths.length);
|
|
1153
|
+
for (let i = 0; i < lengths.length; i++) {
|
|
1154
|
+
const len = lengths[i];
|
|
1155
|
+
if (!len) continue;
|
|
1156
|
+
let c = next[len]++;
|
|
1157
|
+
let r = 0;
|
|
1158
|
+
for (let k = 0; k < len; k++) {
|
|
1159
|
+
r = r << 1 | c & 1;
|
|
1160
|
+
c >>= 1;
|
|
1161
|
+
}
|
|
1162
|
+
codes[i] = r;
|
|
1163
|
+
}
|
|
1164
|
+
return codes;
|
|
1165
|
+
}
|
|
1166
|
+
function lengthSymbol(len) {
|
|
1167
|
+
let i = 28;
|
|
1168
|
+
while (LENGTH_BASE[i] > len) i--;
|
|
1169
|
+
return i;
|
|
1170
|
+
}
|
|
1171
|
+
function distSymbol(d) {
|
|
1172
|
+
let i = 29;
|
|
1173
|
+
while (DIST_BASE[i] > d) i--;
|
|
1174
|
+
return i;
|
|
1175
|
+
}
|
|
1176
|
+
var WINDOW = 32768;
|
|
1177
|
+
var HASH_BITS = 15;
|
|
1178
|
+
var HASH_SIZE = 1 << HASH_BITS;
|
|
1179
|
+
var MAX_CHAIN = 24;
|
|
1180
|
+
var MAX_MATCH = 258;
|
|
1181
|
+
var BLOCK_SYMBOLS = 65536;
|
|
1182
|
+
function deflateRaw(data, level = 1) {
|
|
1183
|
+
const w = new BitWriter(Math.ceil(data.length / 2) + 64);
|
|
1184
|
+
const n = data.length;
|
|
1185
|
+
if (level === 0 || n === 0) {
|
|
1186
|
+
let pos = 0;
|
|
1187
|
+
do {
|
|
1188
|
+
const len = Math.min(65535, n - pos);
|
|
1189
|
+
const final = pos + len >= n ? 1 : 0;
|
|
1190
|
+
w.write(final, 1);
|
|
1191
|
+
w.write(0, 2);
|
|
1192
|
+
w.flush();
|
|
1193
|
+
w.write(len & 65535, 16);
|
|
1194
|
+
w.write(~len & 65535, 16);
|
|
1195
|
+
for (let i2 = 0; i2 < len; i2++) w.write(data[pos + i2], 8);
|
|
1196
|
+
pos += len;
|
|
1197
|
+
} while (pos < n);
|
|
1198
|
+
return w.bytes();
|
|
1199
|
+
}
|
|
1200
|
+
const head = new Int32Array(HASH_SIZE).fill(-1);
|
|
1201
|
+
const prev = new Int32Array(WINDOW);
|
|
1202
|
+
const litLen = new Uint16Array(BLOCK_SYMBOLS);
|
|
1203
|
+
const dists = new Uint16Array(BLOCK_SYMBOLS);
|
|
1204
|
+
let count = 0;
|
|
1205
|
+
const hash = (i2) => (data[i2] << 10 ^ data[i2 + 1] << 5 ^ data[i2 + 2]) * 2654435761 >>> 32 - HASH_BITS;
|
|
1206
|
+
const insert = (i2) => {
|
|
1207
|
+
if (i2 + 2 >= n) return;
|
|
1208
|
+
const h = hash(i2);
|
|
1209
|
+
prev[i2 & WINDOW - 1] = head[h];
|
|
1210
|
+
head[h] = i2;
|
|
1211
|
+
};
|
|
1212
|
+
const flushBlock = (final) => {
|
|
1213
|
+
const litFreq = new Uint32Array(286);
|
|
1214
|
+
const distFreq = new Uint32Array(30);
|
|
1215
|
+
for (let k = 0; k < count; k++) {
|
|
1216
|
+
if (dists[k] === 0) litFreq[litLen[k]]++;
|
|
1217
|
+
else {
|
|
1218
|
+
litFreq[257 + lengthSymbol(litLen[k])]++;
|
|
1219
|
+
distFreq[distSymbol(dists[k])]++;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
litFreq[256] = 1;
|
|
1223
|
+
let anyDist = false;
|
|
1224
|
+
for (const v of distFreq) if (v) anyDist = true;
|
|
1225
|
+
if (!anyDist) distFreq[0] = 1;
|
|
1226
|
+
const litLengths = codeLengths(litFreq, 15);
|
|
1227
|
+
const distLengths = codeLengths(distFreq, 15);
|
|
1228
|
+
let hlit = 286;
|
|
1229
|
+
while (hlit > 257 && !litLengths[hlit - 1]) hlit--;
|
|
1230
|
+
let hdist = 30;
|
|
1231
|
+
while (hdist > 1 && !distLengths[hdist - 1]) hdist--;
|
|
1232
|
+
const all = new Uint8Array(hlit + hdist);
|
|
1233
|
+
all.set(litLengths.subarray(0, hlit));
|
|
1234
|
+
all.set(distLengths.subarray(0, hdist), hlit);
|
|
1235
|
+
const clSyms = [];
|
|
1236
|
+
const clExtra = [];
|
|
1237
|
+
for (let i2 = 0; i2 < all.length; ) {
|
|
1238
|
+
const v = all[i2];
|
|
1239
|
+
let run = 1;
|
|
1240
|
+
while (i2 + run < all.length && all[i2 + run] === v) run++;
|
|
1241
|
+
if (v === 0 && run >= 3) {
|
|
1242
|
+
const r = Math.min(run, 138);
|
|
1243
|
+
if (r >= 11) {
|
|
1244
|
+
clSyms.push(18);
|
|
1245
|
+
clExtra.push(r - 11);
|
|
1246
|
+
} else {
|
|
1247
|
+
clSyms.push(17);
|
|
1248
|
+
clExtra.push(r - 3);
|
|
1249
|
+
}
|
|
1250
|
+
i2 += r;
|
|
1251
|
+
} else if (v !== 0 && run >= 4) {
|
|
1252
|
+
clSyms.push(v);
|
|
1253
|
+
clExtra.push(0);
|
|
1254
|
+
const r = Math.min(run - 1, 6);
|
|
1255
|
+
clSyms.push(16);
|
|
1256
|
+
clExtra.push(r - 3);
|
|
1257
|
+
i2 += r + 1;
|
|
1258
|
+
} else {
|
|
1259
|
+
clSyms.push(v);
|
|
1260
|
+
clExtra.push(0);
|
|
1261
|
+
i2++;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
const clFreq = new Uint32Array(19);
|
|
1265
|
+
for (const s of clSyms) clFreq[s]++;
|
|
1266
|
+
const clLengths = codeLengths(clFreq, 7);
|
|
1267
|
+
let hclen = 19;
|
|
1268
|
+
while (hclen > 4 && !clLengths[CL_ORDER[hclen - 1]]) hclen--;
|
|
1269
|
+
const clCodes = canonicalCodes(clLengths);
|
|
1270
|
+
const litCodes = canonicalCodes(litLengths);
|
|
1271
|
+
const distCodes = canonicalCodes(distLengths);
|
|
1272
|
+
w.write(final ? 1 : 0, 1);
|
|
1273
|
+
w.write(2, 2);
|
|
1274
|
+
w.write(hlit - 257, 5);
|
|
1275
|
+
w.write(hdist - 1, 5);
|
|
1276
|
+
w.write(hclen - 4, 4);
|
|
1277
|
+
for (let i2 = 0; i2 < hclen; i2++) w.write(clLengths[CL_ORDER[i2]], 3);
|
|
1278
|
+
for (let i2 = 0; i2 < clSyms.length; i2++) {
|
|
1279
|
+
const s = clSyms[i2];
|
|
1280
|
+
w.write(clCodes[s], clLengths[s]);
|
|
1281
|
+
if (s === 16) w.write(clExtra[i2], 2);
|
|
1282
|
+
else if (s === 17) w.write(clExtra[i2], 3);
|
|
1283
|
+
else if (s === 18) w.write(clExtra[i2], 7);
|
|
1284
|
+
}
|
|
1285
|
+
for (let k = 0; k < count; k++) {
|
|
1286
|
+
const d = dists[k];
|
|
1287
|
+
if (d === 0) {
|
|
1288
|
+
const s = litLen[k];
|
|
1289
|
+
w.write(litCodes[s], litLengths[s]);
|
|
1290
|
+
} else {
|
|
1291
|
+
const len = litLen[k];
|
|
1292
|
+
const li = lengthSymbol(len);
|
|
1293
|
+
w.write(litCodes[257 + li], litLengths[257 + li]);
|
|
1294
|
+
if (LENGTH_EXTRA[li]) w.write(len - LENGTH_BASE[li], LENGTH_EXTRA[li]);
|
|
1295
|
+
const di = distSymbol(d);
|
|
1296
|
+
w.write(distCodes[di], distLengths[di]);
|
|
1297
|
+
if (DIST_EXTRA[di]) w.write(d - DIST_BASE[di], DIST_EXTRA[di]);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
w.write(litCodes[256], litLengths[256]);
|
|
1301
|
+
count = 0;
|
|
1302
|
+
};
|
|
1303
|
+
let i = 0;
|
|
1304
|
+
while (i < n) {
|
|
1305
|
+
let bestLen = 0;
|
|
1306
|
+
let bestDist = 0;
|
|
1307
|
+
if (i + 2 < n) {
|
|
1308
|
+
let cand = head[hash(i)];
|
|
1309
|
+
let chain = MAX_CHAIN;
|
|
1310
|
+
const limit = Math.min(MAX_MATCH, n - i);
|
|
1311
|
+
while (cand >= 0 && i - cand <= WINDOW - 1 && chain-- > 0) {
|
|
1312
|
+
if (data[cand + bestLen] === data[i + bestLen]) {
|
|
1313
|
+
let l = 0;
|
|
1314
|
+
while (l < limit && data[cand + l] === data[i + l]) l++;
|
|
1315
|
+
if (l > bestLen) {
|
|
1316
|
+
bestLen = l;
|
|
1317
|
+
bestDist = i - cand;
|
|
1318
|
+
if (l === limit) break;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
const next = prev[cand & WINDOW - 1];
|
|
1322
|
+
if (next >= cand) break;
|
|
1323
|
+
cand = next;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
if (bestLen >= 3) {
|
|
1327
|
+
litLen[count] = bestLen;
|
|
1328
|
+
dists[count] = bestDist;
|
|
1329
|
+
count++;
|
|
1330
|
+
const end = i + bestLen;
|
|
1331
|
+
const insertEnd = bestLen > 64 ? i + 64 : end;
|
|
1332
|
+
for (let k = i; k < insertEnd; k++) insert(k);
|
|
1333
|
+
for (let k = Math.max(insertEnd, end - 3); k < end; k++) insert(k);
|
|
1334
|
+
i = end;
|
|
1335
|
+
} else {
|
|
1336
|
+
litLen[count] = data[i];
|
|
1337
|
+
dists[count] = 0;
|
|
1338
|
+
count++;
|
|
1339
|
+
insert(i);
|
|
1340
|
+
i++;
|
|
1341
|
+
}
|
|
1342
|
+
if (count >= BLOCK_SYMBOLS - 1 && i < n) flushBlock(false);
|
|
1343
|
+
}
|
|
1344
|
+
flushBlock(true);
|
|
1345
|
+
w.flush();
|
|
1346
|
+
return w.bytes();
|
|
1347
|
+
}
|
|
1348
|
+
function deflate(data, level = 1) {
|
|
1349
|
+
const raw = deflateRaw(data, level);
|
|
1350
|
+
const out = new Uint8Array(raw.length + 6);
|
|
1351
|
+
out[0] = 120;
|
|
1352
|
+
out[1] = 1;
|
|
1353
|
+
out.set(raw, 2);
|
|
1354
|
+
const a = adler32(data);
|
|
1355
|
+
const t = out.length - 4;
|
|
1356
|
+
out[t] = a >>> 24;
|
|
1357
|
+
out[t + 1] = a >>> 16 & 255;
|
|
1358
|
+
out[t + 2] = a >>> 8 & 255;
|
|
1359
|
+
out[t + 3] = a & 255;
|
|
1360
|
+
return out;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// src/raster/png.ts
|
|
1364
|
+
var SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
1365
|
+
var crcTable = null;
|
|
1366
|
+
function crc32(bytes, start, end) {
|
|
1367
|
+
if (!crcTable) {
|
|
1368
|
+
crcTable = new Uint32Array(256);
|
|
1369
|
+
for (let n = 0; n < 256; n++) {
|
|
1370
|
+
let c2 = n;
|
|
1371
|
+
for (let k = 0; k < 8; k++) c2 = c2 & 1 ? 3988292384 ^ c2 >>> 1 : c2 >>> 1;
|
|
1372
|
+
crcTable[n] = c2 >>> 0;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
let c = 4294967295;
|
|
1376
|
+
for (let i = start; i < end; i++) c = crcTable[(c ^ bytes[i]) & 255] ^ c >>> 8;
|
|
1377
|
+
return (c ^ 4294967295) >>> 0;
|
|
1378
|
+
}
|
|
1379
|
+
function paeth(a, b, c) {
|
|
1380
|
+
const p = a + b - c;
|
|
1381
|
+
const pa = Math.abs(p - a);
|
|
1382
|
+
const pb = Math.abs(p - b);
|
|
1383
|
+
const pc = Math.abs(p - c);
|
|
1384
|
+
return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
|
|
1385
|
+
}
|
|
1386
|
+
function encodePng(width, height, data, channels = 4) {
|
|
1387
|
+
const stride = width * channels;
|
|
1388
|
+
const raw = new Uint8Array((stride + 1) * height);
|
|
1389
|
+
const bpp = channels;
|
|
1390
|
+
const cand = [new Uint8Array(stride), new Uint8Array(stride), new Uint8Array(stride), new Uint8Array(stride), new Uint8Array(stride)];
|
|
1391
|
+
for (let y = 0; y < height; y++) {
|
|
1392
|
+
const row = y * stride;
|
|
1393
|
+
const prevRow = (y - 1) * stride;
|
|
1394
|
+
let best = 0;
|
|
1395
|
+
let bestScore = Infinity;
|
|
1396
|
+
for (let f = 0; f < 5; f++) {
|
|
1397
|
+
const out2 = cand[f];
|
|
1398
|
+
let score = 0;
|
|
1399
|
+
for (let x = 0; x < stride; x++) {
|
|
1400
|
+
const v = data[row + x];
|
|
1401
|
+
const a = x >= bpp ? data[row + x - bpp] : 0;
|
|
1402
|
+
const b = y > 0 ? data[prevRow + x] : 0;
|
|
1403
|
+
const c = x >= bpp && y > 0 ? data[prevRow + x - bpp] : 0;
|
|
1404
|
+
let r;
|
|
1405
|
+
switch (f) {
|
|
1406
|
+
case 0:
|
|
1407
|
+
r = v;
|
|
1408
|
+
break;
|
|
1409
|
+
case 1:
|
|
1410
|
+
r = v - a;
|
|
1411
|
+
break;
|
|
1412
|
+
case 2:
|
|
1413
|
+
r = v - b;
|
|
1414
|
+
break;
|
|
1415
|
+
case 3:
|
|
1416
|
+
r = v - (a + b >> 1);
|
|
1417
|
+
break;
|
|
1418
|
+
default:
|
|
1419
|
+
r = v - paeth(a, b, c);
|
|
1420
|
+
}
|
|
1421
|
+
r &= 255;
|
|
1422
|
+
out2[x] = r;
|
|
1423
|
+
score += r < 128 ? r : 256 - r;
|
|
1424
|
+
if (score >= bestScore) break;
|
|
1425
|
+
}
|
|
1426
|
+
if (score < bestScore) {
|
|
1427
|
+
bestScore = score;
|
|
1428
|
+
best = f;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
const o = y * (stride + 1);
|
|
1432
|
+
raw[o] = best;
|
|
1433
|
+
raw.set(cand[best], o + 1);
|
|
1434
|
+
}
|
|
1435
|
+
const idat = deflate(raw);
|
|
1436
|
+
const chunks = [];
|
|
1437
|
+
const ihdr = new Uint8Array(13);
|
|
1438
|
+
writeU32(ihdr, 0, width);
|
|
1439
|
+
writeU32(ihdr, 4, height);
|
|
1440
|
+
ihdr[8] = 8;
|
|
1441
|
+
ihdr[9] = channels === 1 ? 0 : 6;
|
|
1442
|
+
chunks.push(["IHDR", ihdr], ["IDAT", idat], ["IEND", new Uint8Array(0)]);
|
|
1443
|
+
let size = 8;
|
|
1444
|
+
for (const [, d] of chunks) size += 12 + d.length;
|
|
1445
|
+
const out = new Uint8Array(size);
|
|
1446
|
+
out.set(SIGNATURE, 0);
|
|
1447
|
+
let p = 8;
|
|
1448
|
+
for (const [type, d] of chunks) {
|
|
1449
|
+
writeU32(out, p, d.length);
|
|
1450
|
+
for (let i = 0; i < 4; i++) out[p + 4 + i] = type.charCodeAt(i);
|
|
1451
|
+
out.set(d, p + 8);
|
|
1452
|
+
writeU32(out, p + 8 + d.length, crc32(out, p + 4, p + 8 + d.length));
|
|
1453
|
+
p += 12 + d.length;
|
|
1454
|
+
}
|
|
1455
|
+
return out;
|
|
1456
|
+
}
|
|
1457
|
+
function writeU32(b, o, v) {
|
|
1458
|
+
b[o] = v >>> 24;
|
|
1459
|
+
b[o + 1] = v >>> 16 & 255;
|
|
1460
|
+
b[o + 2] = v >>> 8 & 255;
|
|
1461
|
+
b[o + 3] = v & 255;
|
|
1462
|
+
}
|
|
1463
|
+
function readU32(b, o) {
|
|
1464
|
+
return (b[o] << 24 | b[o + 1] << 16 | b[o + 2] << 8 | b[o + 3]) >>> 0;
|
|
1465
|
+
}
|
|
1466
|
+
function isPng(bytes) {
|
|
1467
|
+
return bytes.length > 8 && SIGNATURE.every((v, i) => bytes[i] === v);
|
|
1468
|
+
}
|
|
1469
|
+
function decodePng(bytes, options = {}) {
|
|
1470
|
+
if (!isPng(bytes)) throw new Error("Not a PNG image.");
|
|
1471
|
+
let p = 8;
|
|
1472
|
+
let width = 0;
|
|
1473
|
+
let height = 0;
|
|
1474
|
+
let depth = 0;
|
|
1475
|
+
let type = 0;
|
|
1476
|
+
let interlace = 0;
|
|
1477
|
+
let palette = null;
|
|
1478
|
+
let trns = null;
|
|
1479
|
+
const idat = [];
|
|
1480
|
+
while (p + 8 <= bytes.length) {
|
|
1481
|
+
const len = readU32(bytes, p);
|
|
1482
|
+
const t = String.fromCharCode(bytes[p + 4], bytes[p + 5], bytes[p + 6], bytes[p + 7]);
|
|
1483
|
+
const body = bytes.subarray(p + 8, p + 8 + len);
|
|
1484
|
+
if (p + 12 + len > bytes.length) throw new Error("Truncated PNG.");
|
|
1485
|
+
if (t === "IHDR") {
|
|
1486
|
+
width = readU32(body, 0);
|
|
1487
|
+
height = readU32(body, 4);
|
|
1488
|
+
depth = body[8];
|
|
1489
|
+
type = body[9];
|
|
1490
|
+
interlace = body[12];
|
|
1491
|
+
} else if (t === "PLTE") palette = body;
|
|
1492
|
+
else if (t === "tRNS") trns = body;
|
|
1493
|
+
else if (t === "IDAT") idat.push(body);
|
|
1494
|
+
else if (t === "IEND") break;
|
|
1495
|
+
p += 12 + len;
|
|
1496
|
+
}
|
|
1497
|
+
if (!width || !height) throw new Error("PNG has no image header.");
|
|
1498
|
+
if (interlace) throw new Error("Interlaced PNGs are not supported here; decode them with a canvas.");
|
|
1499
|
+
const maxPixels = options.maxPixels ?? 1 << 28;
|
|
1500
|
+
if (width * height > maxPixels) throw new Error(`PNG is too large (${width}\xD7${height}).`);
|
|
1501
|
+
const samples = type === 0 ? 1 : type === 2 ? 3 : type === 3 ? 1 : type === 4 ? 2 : type === 6 ? 4 : 0;
|
|
1502
|
+
if (!samples || ![1, 2, 4, 8, 16].includes(depth)) throw new Error(`Unsupported PNG format (type ${type}, depth ${depth}).`);
|
|
1503
|
+
let total = 0;
|
|
1504
|
+
for (const c of idat) total += c.length;
|
|
1505
|
+
const z = new Uint8Array(total);
|
|
1506
|
+
let o = 0;
|
|
1507
|
+
for (const c of idat) {
|
|
1508
|
+
z.set(c, o);
|
|
1509
|
+
o += c.length;
|
|
1510
|
+
}
|
|
1511
|
+
const bitsPerPixel = samples * depth;
|
|
1512
|
+
const stride = Math.ceil(width * bitsPerPixel / 8);
|
|
1513
|
+
const raw = inflate(z, { maxOutput: (stride + 1) * height, sizeHint: (stride + 1) * height });
|
|
1514
|
+
if (raw.length < (stride + 1) * height) throw new Error("PNG image data is truncated.");
|
|
1515
|
+
const bpp = Math.max(1, bitsPerPixel >> 3);
|
|
1516
|
+
const lines = new Uint8Array(stride * height);
|
|
1517
|
+
for (let y = 0; y < height; y++) {
|
|
1518
|
+
const f = raw[y * (stride + 1)];
|
|
1519
|
+
const src = y * (stride + 1) + 1;
|
|
1520
|
+
const dst = y * stride;
|
|
1521
|
+
const up = dst - stride;
|
|
1522
|
+
for (let x = 0; x < stride; x++) {
|
|
1523
|
+
const v = raw[src + x];
|
|
1524
|
+
const a = x >= bpp ? lines[dst + x - bpp] : 0;
|
|
1525
|
+
const b = y > 0 ? lines[up + x] : 0;
|
|
1526
|
+
const c = x >= bpp && y > 0 ? lines[up + x - bpp] : 0;
|
|
1527
|
+
let r;
|
|
1528
|
+
switch (f) {
|
|
1529
|
+
case 0:
|
|
1530
|
+
r = v;
|
|
1531
|
+
break;
|
|
1532
|
+
case 1:
|
|
1533
|
+
r = v + a;
|
|
1534
|
+
break;
|
|
1535
|
+
case 2:
|
|
1536
|
+
r = v + b;
|
|
1537
|
+
break;
|
|
1538
|
+
case 3:
|
|
1539
|
+
r = v + (a + b >> 1);
|
|
1540
|
+
break;
|
|
1541
|
+
case 4:
|
|
1542
|
+
r = v + paeth(a, b, c);
|
|
1543
|
+
break;
|
|
1544
|
+
default:
|
|
1545
|
+
throw new Error(`Invalid PNG filter ${f}.`);
|
|
1546
|
+
}
|
|
1547
|
+
lines[dst + x] = r & 255;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
const sample = (y, i) => {
|
|
1551
|
+
const row = y * stride;
|
|
1552
|
+
if (depth === 8) return lines[row + i];
|
|
1553
|
+
if (depth === 16) return lines[row + i * 2];
|
|
1554
|
+
const perByte = 8 / depth;
|
|
1555
|
+
const byte = lines[row + Math.floor(i / perByte)];
|
|
1556
|
+
const shift = 8 - depth * (i % perByte + 1);
|
|
1557
|
+
const v = byte >> shift & (1 << depth) - 1;
|
|
1558
|
+
return type === 3 ? v : Math.round(v * 255 / ((1 << depth) - 1));
|
|
1559
|
+
};
|
|
1560
|
+
const gray = options.keepGray && type === 0 && !trns;
|
|
1561
|
+
const channels = gray ? 1 : 4;
|
|
1562
|
+
const out = new Uint8ClampedArray(width * height * channels);
|
|
1563
|
+
const trnsGray = trns && type === 0 ? depth === 16 ? trns[0] : sample16(trns, 0, depth) : -1;
|
|
1564
|
+
const trnsRgb = trns && type === 2 ? [0, 1, 2].map((k) => depth === 16 ? trns[k * 2] : sample16(trns, k, depth)) : null;
|
|
1565
|
+
for (let y = 0; y < height; y++) {
|
|
1566
|
+
for (let x = 0; x < width; x++) {
|
|
1567
|
+
const d = (y * width + x) * channels;
|
|
1568
|
+
if (type === 0) {
|
|
1569
|
+
const v = sample(y, x);
|
|
1570
|
+
if (gray) out[d] = v;
|
|
1571
|
+
else {
|
|
1572
|
+
out[d] = out[d + 1] = out[d + 2] = v;
|
|
1573
|
+
out[d + 3] = v === trnsGray && trns ? 0 : 255;
|
|
1574
|
+
}
|
|
1575
|
+
} else if (type === 2) {
|
|
1576
|
+
const r = sample(y, x * 3);
|
|
1577
|
+
const g = sample(y, x * 3 + 1);
|
|
1578
|
+
const b = sample(y, x * 3 + 2);
|
|
1579
|
+
out[d] = r;
|
|
1580
|
+
out[d + 1] = g;
|
|
1581
|
+
out[d + 2] = b;
|
|
1582
|
+
out[d + 3] = trnsRgb && r === trnsRgb[0] && g === trnsRgb[1] && b === trnsRgb[2] ? 0 : 255;
|
|
1583
|
+
} else if (type === 3) {
|
|
1584
|
+
const i = sample(y, x);
|
|
1585
|
+
if (!palette || i * 3 + 2 >= palette.length) throw new Error("PNG palette index out of range.");
|
|
1586
|
+
out[d] = palette[i * 3];
|
|
1587
|
+
out[d + 1] = palette[i * 3 + 1];
|
|
1588
|
+
out[d + 2] = palette[i * 3 + 2];
|
|
1589
|
+
out[d + 3] = trns && i < trns.length ? trns[i] : 255;
|
|
1590
|
+
} else if (type === 4) {
|
|
1591
|
+
const v = sample(y, x * 2);
|
|
1592
|
+
out[d] = out[d + 1] = out[d + 2] = v;
|
|
1593
|
+
out[d + 3] = sample(y, x * 2 + 1);
|
|
1594
|
+
} else {
|
|
1595
|
+
out[d] = sample(y, x * 4);
|
|
1596
|
+
out[d + 1] = sample(y, x * 4 + 1);
|
|
1597
|
+
out[d + 2] = sample(y, x * 4 + 2);
|
|
1598
|
+
out[d + 3] = sample(y, x * 4 + 3);
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
return { width, height, channels, data: out };
|
|
1603
|
+
}
|
|
1604
|
+
function sample16(trns, k, depth) {
|
|
1605
|
+
const v = trns[k * 2] << 8 | trns[k * 2 + 1];
|
|
1606
|
+
if (depth === 8) return v & 255;
|
|
1607
|
+
return Math.round((v & (1 << depth) - 1) * 255 / ((1 << depth) - 1));
|
|
1608
|
+
}
|
|
1609
|
+
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
1610
|
+
var B64_INV = null;
|
|
1611
|
+
function bytesToBase64(bytes) {
|
|
1612
|
+
const parts = [];
|
|
1613
|
+
const CHUNK = 3 * 4096;
|
|
1614
|
+
for (let start = 0; start < bytes.length; start += CHUNK) {
|
|
1615
|
+
const end = Math.min(bytes.length, start + CHUNK);
|
|
1616
|
+
let s = "";
|
|
1617
|
+
let i = start;
|
|
1618
|
+
for (; i + 2 < end; i += 3) {
|
|
1619
|
+
const n = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
|
|
1620
|
+
s += B64[n >> 18] + B64[n >> 12 & 63] + B64[n >> 6 & 63] + B64[n & 63];
|
|
1621
|
+
}
|
|
1622
|
+
if (i < end) {
|
|
1623
|
+
const n = bytes[i] << 16 | (i + 1 < end ? bytes[i + 1] << 8 : 0);
|
|
1624
|
+
s += B64[n >> 18] + B64[n >> 12 & 63] + (i + 1 < end ? B64[n >> 6 & 63] : "=") + "=";
|
|
1625
|
+
}
|
|
1626
|
+
parts.push(s);
|
|
1627
|
+
}
|
|
1628
|
+
return parts.join("");
|
|
1629
|
+
}
|
|
1630
|
+
function base64ToBytes(text) {
|
|
1631
|
+
if (!B64_INV) {
|
|
1632
|
+
B64_INV = new Int16Array(128).fill(-1);
|
|
1633
|
+
for (let i = 0; i < 64; i++) B64_INV[B64.charCodeAt(i)] = i;
|
|
1634
|
+
B64_INV["-".charCodeAt(0)] = 62;
|
|
1635
|
+
B64_INV["_".charCodeAt(0)] = 63;
|
|
1636
|
+
}
|
|
1637
|
+
const inv = B64_INV;
|
|
1638
|
+
const clean = text.replace(/[\s=]/g, "");
|
|
1639
|
+
const out = new Uint8Array(Math.floor(clean.length * 3 / 4));
|
|
1640
|
+
let bits = 0;
|
|
1641
|
+
let acc = 0;
|
|
1642
|
+
let o = 0;
|
|
1643
|
+
for (let i = 0; i < clean.length; i++) {
|
|
1644
|
+
const c = clean.charCodeAt(i);
|
|
1645
|
+
const v = c < 128 ? inv[c] : -1;
|
|
1646
|
+
if (v < 0) throw new Error("Invalid base64 data.");
|
|
1647
|
+
acc = acc << 6 | v;
|
|
1648
|
+
bits += 6;
|
|
1649
|
+
if (bits >= 8) {
|
|
1650
|
+
bits -= 8;
|
|
1651
|
+
out[o++] = acc >> bits & 255;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return out.subarray(0, o);
|
|
1655
|
+
}
|
|
1656
|
+
function parseDataUrl(url) {
|
|
1657
|
+
const m = /^data:([^;,]*)((?:;[^;,]*)*?),/i.exec(url);
|
|
1658
|
+
if (!m) return null;
|
|
1659
|
+
const body = url.slice(m[0].length);
|
|
1660
|
+
const isBase64 = /;base64/i.test(m[2]);
|
|
1661
|
+
if (isBase64) return { mime: m[1] || "text/plain", bytes: base64ToBytes(body) };
|
|
1662
|
+
const text = decodeURIComponent(body);
|
|
1663
|
+
const bytes = new Uint8Array(text.length);
|
|
1664
|
+
for (let i = 0; i < text.length; i++) bytes[i] = text.charCodeAt(i) & 255;
|
|
1665
|
+
return { mime: m[1] || "text/plain", bytes };
|
|
1666
|
+
}
|
|
1667
|
+
function pngDataUrl(width, height, data, channels = 4) {
|
|
1668
|
+
return `data:image/png;base64,${bytesToBase64(encodePng(width, height, data, channels))}`;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
export {
|
|
1672
|
+
DIRECT_GAUSSIAN_MAX_SIGMA,
|
|
1673
|
+
gaussianBlur,
|
|
1674
|
+
flattenLayers,
|
|
1675
|
+
findLayer,
|
|
1676
|
+
mapLayer,
|
|
1677
|
+
insertLayer,
|
|
1678
|
+
isLayerLocked,
|
|
1679
|
+
blendTile,
|
|
1680
|
+
RasterCompositor,
|
|
1681
|
+
changedTiles,
|
|
1682
|
+
ZlibError,
|
|
1683
|
+
inflateRaw,
|
|
1684
|
+
adler32,
|
|
1685
|
+
inflate,
|
|
1686
|
+
deflateRaw,
|
|
1687
|
+
deflate,
|
|
1688
|
+
encodePng,
|
|
1689
|
+
isPng,
|
|
1690
|
+
decodePng,
|
|
1691
|
+
bytesToBase64,
|
|
1692
|
+
base64ToBytes,
|
|
1693
|
+
parseDataUrl,
|
|
1694
|
+
pngDataUrl
|
|
1695
|
+
};
|
|
1696
|
+
//# sourceMappingURL=chunk-P5TPUNBZ.js.map
|