@pixerate/editor 0.183.0 → 0.186.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/dist/{chunk-Y46OMERN.js → chunk-FW7N64XK.js} +298 -189
- package/dist/{chunk-UHMSMUHK.cjs → chunk-X2KQQZBT.cjs} +358 -249
- package/dist/image-editor/controller.d.cts +208 -8
- package/dist/image-editor/controller.d.ts +208 -8
- package/dist/image-editor/index.cjs +2610 -1025
- package/dist/image-editor/index.js +1672 -87
- package/dist/image-editor/layers/model.d.cts +5 -1
- package/dist/image-editor/layers/model.d.ts +5 -1
- package/dist/image-editor/layers/paint-ui.d.cts +2 -1
- package/dist/image-editor/layers/paint-ui.d.ts +2 -1
- package/dist/image-editor/layers/panel.d.cts +23 -1
- package/dist/image-editor/layers/panel.d.ts +23 -1
- package/dist/image-editor/layers/serialize.d.cts +3 -0
- package/dist/image-editor/layers/serialize.d.ts +3 -0
- package/dist/image-editor/psd/channels.d.cts +24 -0
- package/dist/image-editor/psd/channels.d.ts +24 -0
- package/dist/image-editor/psd/export.d.cts +3 -0
- package/dist/image-editor/psd/export.d.ts +3 -0
- package/dist/image-editor/psd/import.d.cts +10 -0
- package/dist/image-editor/psd/import.d.ts +10 -0
- package/dist/image-editor/renderer.d.cts +6 -0
- package/dist/image-editor/renderer.d.ts +6 -0
- package/dist/image-editor/selection/channels.d.cts +52 -0
- package/dist/image-editor/selection/channels.d.ts +52 -0
- package/dist/image-editor/selection/gesture.d.cts +47 -2
- package/dist/image-editor/selection/gesture.d.ts +47 -2
- package/dist/image-editor/selection/index.d.cts +2 -0
- package/dist/image-editor/selection/index.d.ts +2 -0
- package/dist/image-editor/selection/modify.d.cts +3 -2
- package/dist/image-editor/selection/modify.d.ts +3 -2
- package/dist/image-editor/selection/overlay.d.cts +2 -0
- package/dist/image-editor/selection/overlay.d.ts +2 -0
- package/dist/image-editor/selection/stroke.d.cts +8 -0
- package/dist/image-editor/selection/stroke.d.ts +8 -0
- package/dist/image-editor/selection/tool-options.d.cts +12 -2
- package/dist/image-editor/selection/tool-options.d.ts +12 -2
- package/dist/image-editor/selection/wand.d.cts +37 -0
- package/dist/image-editor/selection/wand.d.ts +37 -0
- package/dist/image-editor/selection-tools.d.cts +8 -3
- package/dist/image-editor/selection-tools.d.ts +8 -3
- package/dist/image-editor/tools.d.cts +10 -2
- package/dist/image-editor/tools.d.ts +10 -2
- package/dist/image-editor/types.d.cts +12 -0
- package/dist/image-editor/types.d.ts +12 -0
- package/dist/paint-editor/index.cjs +6 -6
- package/dist/paint-editor/index.js +1 -1
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +19 -21
- package/skills/pixerate-image-editor/references/free-transform.md +22 -0
- package/skills/pixerate-image-selection-masking/SKILL.md +36 -3
|
@@ -1192,7 +1192,7 @@ function shiftCoverage(src, w, h, amount) {
|
|
|
1192
1192
|
for (let i = 0; i < lo.length; i++) lo[i] += (hi[i] - lo[i]) * frac;
|
|
1193
1193
|
return lo;
|
|
1194
1194
|
}
|
|
1195
|
-
var SELECTION_MODIFY_OPS = ["
|
|
1195
|
+
var SELECTION_MODIFY_OPS = ["expand", "contract", "feather", "border", "smooth", "grow", "shrink"];
|
|
1196
1196
|
function wrap(mask, coverage) {
|
|
1197
1197
|
const out = new Uint8Array(coverage.length);
|
|
1198
1198
|
for (let i = 0; i < out.length; i++) out[i] = coverageToByte(coverage[i]);
|
|
@@ -1204,8 +1204,10 @@ function modifySelection(mask, op, radius) {
|
|
|
1204
1204
|
const m = selectionToCoverage(mask);
|
|
1205
1205
|
const { width: w, height: h } = mask;
|
|
1206
1206
|
switch (op) {
|
|
1207
|
+
case "expand":
|
|
1207
1208
|
case "grow":
|
|
1208
1209
|
return wrap(mask, expandCoverage(m, w, h, r));
|
|
1210
|
+
case "contract":
|
|
1209
1211
|
case "shrink":
|
|
1210
1212
|
return wrap(mask, contractCoverage(m, w, h, r));
|
|
1211
1213
|
case "feather":
|
|
@@ -6344,6 +6346,192 @@ function encodeLegacyName(s) {
|
|
|
6344
6346
|
return Uint8Array.from(out);
|
|
6345
6347
|
}
|
|
6346
6348
|
|
|
6349
|
+
// src/image-editor/psd/compression.ts
|
|
6350
|
+
var MAX_DECODED_BYTES = 2 ** 31;
|
|
6351
|
+
function rowBytes(width, depth) {
|
|
6352
|
+
return depth === 1 ? Math.ceil(width / 8) : width * Math.max(1, depth >> 3);
|
|
6353
|
+
}
|
|
6354
|
+
function decodedLength(l) {
|
|
6355
|
+
const total = l.planes * l.height * rowBytes(l.width, l.depth);
|
|
6356
|
+
if (total > MAX_DECODED_BYTES) throw new PsdError(`Decoded channel data of ${total} bytes is larger than the ${MAX_DECODED_BYTES}-byte limit.`);
|
|
6357
|
+
return total;
|
|
6358
|
+
}
|
|
6359
|
+
function packbitsBound(n) {
|
|
6360
|
+
return n + Math.ceil(n / 128);
|
|
6361
|
+
}
|
|
6362
|
+
function packbitsEncode(src, out, at) {
|
|
6363
|
+
const n = src.length;
|
|
6364
|
+
let i = 0;
|
|
6365
|
+
while (i < n) {
|
|
6366
|
+
let run = 1;
|
|
6367
|
+
while (i + run < n && run < 128 && src[i + run] === src[i]) run++;
|
|
6368
|
+
if (run >= 2) {
|
|
6369
|
+
out[at++] = 257 - run & 255;
|
|
6370
|
+
out[at++] = src[i];
|
|
6371
|
+
i += run;
|
|
6372
|
+
continue;
|
|
6373
|
+
}
|
|
6374
|
+
const start = i;
|
|
6375
|
+
let len = 0;
|
|
6376
|
+
while (i < n && len < 128) {
|
|
6377
|
+
if (i + 2 < n && src[i] === src[i + 1] && src[i] === src[i + 2]) break;
|
|
6378
|
+
i++;
|
|
6379
|
+
len++;
|
|
6380
|
+
}
|
|
6381
|
+
out[at++] = len - 1;
|
|
6382
|
+
out.set(src.subarray(start, start + len), at);
|
|
6383
|
+
at += len;
|
|
6384
|
+
}
|
|
6385
|
+
return at;
|
|
6386
|
+
}
|
|
6387
|
+
function packbitsDecodeInto(src, expected, out, at) {
|
|
6388
|
+
const target = at + expected;
|
|
6389
|
+
let i = 0;
|
|
6390
|
+
while (at < target) {
|
|
6391
|
+
if (i >= src.length) throw new PsdError("PackBits row ended early.");
|
|
6392
|
+
const h = src[i++];
|
|
6393
|
+
if (h < 128) {
|
|
6394
|
+
const len = h + 1;
|
|
6395
|
+
if (i + len > src.length) throw new PsdError("PackBits literal is truncated.");
|
|
6396
|
+
if (at + len > target) throw new PsdError("PackBits literal overflows its row.");
|
|
6397
|
+
out.set(src.subarray(i, i + len), at);
|
|
6398
|
+
at += len;
|
|
6399
|
+
i += len;
|
|
6400
|
+
} else if (h !== 128) {
|
|
6401
|
+
const len = 257 - h;
|
|
6402
|
+
if (i >= src.length) throw new PsdError("PackBits run is truncated.");
|
|
6403
|
+
const b = src[i++];
|
|
6404
|
+
if (at + len > target) throw new PsdError("PackBits run overflows its row.");
|
|
6405
|
+
out.fill(b, at, at + len);
|
|
6406
|
+
at += len;
|
|
6407
|
+
}
|
|
6408
|
+
}
|
|
6409
|
+
return at;
|
|
6410
|
+
}
|
|
6411
|
+
function inflateChecked(data, expected) {
|
|
6412
|
+
let out;
|
|
6413
|
+
try {
|
|
6414
|
+
out = inflate(data, { maxOutput: expected + (1 << 20), sizeHint: expected, verify: false });
|
|
6415
|
+
} catch (e) {
|
|
6416
|
+
throw new PsdError(`ZIP channel data: ${e instanceof ZlibError || e instanceof Error ? e.message : String(e)}`);
|
|
6417
|
+
}
|
|
6418
|
+
if (out.length < expected) throw new PsdError(`ZIP stream produced ${out.length} of ${expected} bytes.`);
|
|
6419
|
+
return out.length === expected ? out : out.subarray(0, expected);
|
|
6420
|
+
}
|
|
6421
|
+
function unpredict(buf, l) {
|
|
6422
|
+
const rb = rowBytes(l.width, l.depth);
|
|
6423
|
+
if (rb === 0) return;
|
|
6424
|
+
const w = l.width;
|
|
6425
|
+
const scratch = l.depth === 32 ? new Uint8Array(rb) : null;
|
|
6426
|
+
for (let o = 0; o + rb <= buf.length; o += rb) {
|
|
6427
|
+
if (l.depth === 1 || l.depth === 8) {
|
|
6428
|
+
for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
|
|
6429
|
+
} else if (l.depth === 16) {
|
|
6430
|
+
for (let i = 1; i < w; i++) {
|
|
6431
|
+
const a = buf[o + 2 * i] << 8 | buf[o + 2 * i + 1];
|
|
6432
|
+
const b = buf[o + 2 * i - 2] << 8 | buf[o + 2 * i - 1];
|
|
6433
|
+
const v = a + b & 65535;
|
|
6434
|
+
buf[o + 2 * i] = v >> 8;
|
|
6435
|
+
buf[o + 2 * i + 1] = v & 255;
|
|
6436
|
+
}
|
|
6437
|
+
} else if (l.depth === 32) {
|
|
6438
|
+
for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
|
|
6439
|
+
scratch.set(buf.subarray(o, o + rb));
|
|
6440
|
+
for (let i = 0; i < w; i++) for (let k = 0; k < 4; k++) buf[o + 4 * i + k] = scratch[k * w + i];
|
|
6441
|
+
} else {
|
|
6442
|
+
throw new PsdError(`ZIP prediction is not defined for depth ${l.depth}.`);
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
function decodeRle(r, l, total) {
|
|
6447
|
+
const rows = l.planes * l.height;
|
|
6448
|
+
const rb = rowBytes(l.width, l.depth);
|
|
6449
|
+
r.checkCount(rows, l.psb ? 4 : 2, "RLE row counts");
|
|
6450
|
+
const counts = new Array(rows);
|
|
6451
|
+
for (let i = 0; i < rows; i++) counts[i] = l.psb ? r.u32() : r.u16();
|
|
6452
|
+
if (total > r.remaining * 130) throw new PsdError("RLE data is too short for its declared size.");
|
|
6453
|
+
const out = new Uint8Array(total);
|
|
6454
|
+
let at = 0;
|
|
6455
|
+
for (const c of counts) at = packbitsDecodeInto(r.bytes(c), rb, out, at);
|
|
6456
|
+
return out;
|
|
6457
|
+
}
|
|
6458
|
+
function decodePlanes(compression, data, l) {
|
|
6459
|
+
const total = decodedLength(l);
|
|
6460
|
+
switch (compression) {
|
|
6461
|
+
case 0:
|
|
6462
|
+
if (data.length < total) throw new PsdError(`Raw channel data has ${data.length} of ${total} bytes.`);
|
|
6463
|
+
return data.slice(0, total);
|
|
6464
|
+
case 1:
|
|
6465
|
+
return decodeRle(new PsdReader(data), l, total);
|
|
6466
|
+
case 2:
|
|
6467
|
+
return inflateChecked(data, total).slice();
|
|
6468
|
+
case 3: {
|
|
6469
|
+
const v = inflateChecked(data, total).slice();
|
|
6470
|
+
unpredict(v, l);
|
|
6471
|
+
return v;
|
|
6472
|
+
}
|
|
6473
|
+
default:
|
|
6474
|
+
throw new PsdError(`Unsupported compression method ${compression}.`);
|
|
6475
|
+
}
|
|
6476
|
+
}
|
|
6477
|
+
function encodeRle(decoded2, l) {
|
|
6478
|
+
const rows = l.planes * l.height;
|
|
6479
|
+
const rb = rowBytes(l.width, l.depth);
|
|
6480
|
+
const cs = l.psb ? 4 : 2;
|
|
6481
|
+
const packed = new Uint8Array(rows * packbitsBound(rb));
|
|
6482
|
+
let at = 0;
|
|
6483
|
+
const counts = new Uint8Array(rows * cs);
|
|
6484
|
+
for (let row = 0; row < rows; row++) {
|
|
6485
|
+
const start = at;
|
|
6486
|
+
at = packbitsEncode(decoded2.subarray(row * rb, (row + 1) * rb), packed, at);
|
|
6487
|
+
const n = at - start;
|
|
6488
|
+
if (cs === 2) {
|
|
6489
|
+
if (n > 65535) throw new PsdError("An RLE row is longer than 65535 bytes; write a PSB instead.");
|
|
6490
|
+
counts[row * 2] = n >> 8;
|
|
6491
|
+
counts[row * 2 + 1] = n & 255;
|
|
6492
|
+
} else {
|
|
6493
|
+
counts[row * 4] = n >>> 24;
|
|
6494
|
+
counts[row * 4 + 1] = n >>> 16 & 255;
|
|
6495
|
+
counts[row * 4 + 2] = n >>> 8 & 255;
|
|
6496
|
+
counts[row * 4 + 3] = n & 255;
|
|
6497
|
+
}
|
|
6498
|
+
}
|
|
6499
|
+
const out = new Uint8Array(counts.length + at);
|
|
6500
|
+
out.set(counts);
|
|
6501
|
+
out.set(packed.subarray(0, at), counts.length);
|
|
6502
|
+
return out;
|
|
6503
|
+
}
|
|
6504
|
+
function predict(buf, l) {
|
|
6505
|
+
const rb = rowBytes(l.width, l.depth);
|
|
6506
|
+
if (rb === 0) return;
|
|
6507
|
+
const w = l.width;
|
|
6508
|
+
const scratch = l.depth === 32 ? new Uint8Array(rb) : null;
|
|
6509
|
+
for (let o = 0; o + rb <= buf.length; o += rb) {
|
|
6510
|
+
if (l.depth === 1 || l.depth === 8) {
|
|
6511
|
+
for (let i = rb - 1; i > 0; i--) buf[o + i] = buf[o + i] - buf[o + i - 1] & 255;
|
|
6512
|
+
} else if (l.depth === 16) {
|
|
6513
|
+
for (let i = w - 1; i > 0; i--) {
|
|
6514
|
+
const a = buf[o + 2 * i] << 8 | buf[o + 2 * i + 1];
|
|
6515
|
+
const b = buf[o + 2 * i - 2] << 8 | buf[o + 2 * i - 1];
|
|
6516
|
+
const v = a - b & 65535;
|
|
6517
|
+
buf[o + 2 * i] = v >> 8;
|
|
6518
|
+
buf[o + 2 * i + 1] = v & 255;
|
|
6519
|
+
}
|
|
6520
|
+
} else if (l.depth === 32) {
|
|
6521
|
+
scratch.set(buf.subarray(o, o + rb));
|
|
6522
|
+
for (let i = 0; i < w; i++) for (let k = 0; k < 4; k++) buf[o + k * w + i] = scratch[4 * i + k];
|
|
6523
|
+
for (let i = rb - 1; i > 0; i--) buf[o + i] = buf[o + i] - buf[o + i - 1] & 255;
|
|
6524
|
+
} else {
|
|
6525
|
+
throw new PsdError(`ZIP prediction is not defined for depth ${l.depth}.`);
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6528
|
+
}
|
|
6529
|
+
function encodeZipPredicted(decoded2, l) {
|
|
6530
|
+
const buf = decoded2.slice();
|
|
6531
|
+
predict(buf, l);
|
|
6532
|
+
return deflate(buf);
|
|
6533
|
+
}
|
|
6534
|
+
|
|
6347
6535
|
// src/image-editor/psd/descriptor.ts
|
|
6348
6536
|
var MAX_DEPTH = 64;
|
|
6349
6537
|
var STRING_IDS = /* @__PURE__ */ new Set(["warp", "list", "time"]);
|
|
@@ -6943,6 +7131,97 @@ function writeAdjustmentBlock(kind, params) {
|
|
|
6943
7131
|
throw new Error(`Unknown adjustment kind "${kind}".`);
|
|
6944
7132
|
}
|
|
6945
7133
|
|
|
7134
|
+
// src/image-editor/psd/channels.ts
|
|
7135
|
+
var hexToRgb2 = (hex) => {
|
|
7136
|
+
const v = parseInt(/^#?([0-9a-f]{6})$/i.exec(hex)?.[1] ?? "ff0000", 16);
|
|
7137
|
+
return [v >> 16 & 255, v >> 8 & 255, v & 255];
|
|
7138
|
+
};
|
|
7139
|
+
var rgbToHex = (r, g, b) => `#${[r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("")}`;
|
|
7140
|
+
function alphaChannelResources(channels) {
|
|
7141
|
+
if (!channels.length) return [];
|
|
7142
|
+
const names = new PsdWriter();
|
|
7143
|
+
for (const c of channels) names.pascal(encodeLegacyName(c.name), 1);
|
|
7144
|
+
const unicode = new PsdWriter();
|
|
7145
|
+
for (const c of channels) unicode.unicode(c.name);
|
|
7146
|
+
const display = new PsdWriter();
|
|
7147
|
+
display.u32(1);
|
|
7148
|
+
for (const c of channels) {
|
|
7149
|
+
const [r, g, b] = hexToRgb2(c.color);
|
|
7150
|
+
display.u16(0);
|
|
7151
|
+
for (const v of [r, g, b, 0]) display.u16(v * 257);
|
|
7152
|
+
display.u16(Math.round(Math.min(1, Math.max(0, c.opacity)) * 100));
|
|
7153
|
+
display.u8(c.indicates === "selected" ? 0 : 1);
|
|
7154
|
+
display.u8(0);
|
|
7155
|
+
}
|
|
7156
|
+
return [
|
|
7157
|
+
{ id: 1006, name: "", data: names.result() },
|
|
7158
|
+
{ id: 1045, name: "", data: unicode.result() },
|
|
7159
|
+
{ id: 1077, name: "", data: display.result() }
|
|
7160
|
+
];
|
|
7161
|
+
}
|
|
7162
|
+
function planeTo8(plane, n, depth) {
|
|
7163
|
+
if (depth === 8) return plane.slice(0, n);
|
|
7164
|
+
const out = new Uint8Array(n);
|
|
7165
|
+
const view = new DataView(plane.buffer, plane.byteOffset, plane.byteLength);
|
|
7166
|
+
for (let i = 0; i < n; i++) {
|
|
7167
|
+
if (depth === 16) out[i] = plane[2 * i];
|
|
7168
|
+
else out[i] = Math.round(Math.min(1, Math.max(0, view.getFloat32(4 * i, false))) * 255);
|
|
7169
|
+
}
|
|
7170
|
+
return out;
|
|
7171
|
+
}
|
|
7172
|
+
function readAlphaChannels(planes, header, first, resource2, warn) {
|
|
7173
|
+
const count = header.channels - first;
|
|
7174
|
+
if (count <= 0 || header.depth === 1) return [];
|
|
7175
|
+
const size = rowBytes(header.width, header.depth) * header.height;
|
|
7176
|
+
const n = header.width * header.height;
|
|
7177
|
+
const names = [];
|
|
7178
|
+
const unicode = resource2(1045);
|
|
7179
|
+
if (unicode) {
|
|
7180
|
+
const r = new PsdReader(unicode);
|
|
7181
|
+
while (r.remaining >= 4 && names.length < count) names.push(r.unicode().replace(/\0+$/, ""));
|
|
7182
|
+
} else {
|
|
7183
|
+
const legacy = resource2(1006);
|
|
7184
|
+
if (legacy) {
|
|
7185
|
+
const r = new PsdReader(legacy);
|
|
7186
|
+
while (r.remaining >= 1 && names.length < count) names.push(new TextDecoder("latin1").decode(r.pascal(1)));
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
const display = [];
|
|
7190
|
+
const info = resource2(1077);
|
|
7191
|
+
if (info && info.length >= 4) {
|
|
7192
|
+
const r = new PsdReader(info);
|
|
7193
|
+
r.u32();
|
|
7194
|
+
while (r.remaining >= 14 && display.length < count) {
|
|
7195
|
+
r.u16();
|
|
7196
|
+
const [cr, cg, cb] = [r.u16(), r.u16(), r.u16()];
|
|
7197
|
+
r.u16();
|
|
7198
|
+
const opacity = r.u16();
|
|
7199
|
+
const kind = r.u8();
|
|
7200
|
+
r.u8();
|
|
7201
|
+
display.push({ color: rgbToHex(cr >> 8, cg >> 8, cb >> 8), opacity: Math.min(100, opacity) / 100, kind });
|
|
7202
|
+
}
|
|
7203
|
+
}
|
|
7204
|
+
const out = [];
|
|
7205
|
+
for (let i = 0; i < count; i++) {
|
|
7206
|
+
const start = (first + i) * size;
|
|
7207
|
+
if (start + size > planes.length) break;
|
|
7208
|
+
const d = display[i];
|
|
7209
|
+
const name = names[i] || `Alpha ${i + 1}`;
|
|
7210
|
+
if (d?.kind === 2) {
|
|
7211
|
+
warn(`Spot colour channel "${name}" is not supported and was left out.`);
|
|
7212
|
+
continue;
|
|
7213
|
+
}
|
|
7214
|
+
out.push({
|
|
7215
|
+
name,
|
|
7216
|
+
data: planeTo8(planes.subarray(start, start + size), n, header.depth),
|
|
7217
|
+
color: d?.color ?? "#ff0000",
|
|
7218
|
+
opacity: d?.opacity ?? 0.5,
|
|
7219
|
+
indicates: d?.kind === 0 ? "selected" : "masked"
|
|
7220
|
+
});
|
|
7221
|
+
}
|
|
7222
|
+
return out;
|
|
7223
|
+
}
|
|
7224
|
+
|
|
6946
7225
|
// src/image-editor/psd/paths.ts
|
|
6947
7226
|
var RECORD = 26;
|
|
6948
7227
|
var fixed = (v) => v / 16777216;
|
|
@@ -8127,192 +8406,6 @@ function tyShForLayer(layer) {
|
|
|
8127
8406
|
return writeTySh({ transform: textMatrix(layer), text: text3, warp, tail: new Uint8Array(16) });
|
|
8128
8407
|
}
|
|
8129
8408
|
|
|
8130
|
-
// src/image-editor/psd/compression.ts
|
|
8131
|
-
var MAX_DECODED_BYTES = 2 ** 31;
|
|
8132
|
-
function rowBytes(width, depth) {
|
|
8133
|
-
return depth === 1 ? Math.ceil(width / 8) : width * Math.max(1, depth >> 3);
|
|
8134
|
-
}
|
|
8135
|
-
function decodedLength(l) {
|
|
8136
|
-
const total = l.planes * l.height * rowBytes(l.width, l.depth);
|
|
8137
|
-
if (total > MAX_DECODED_BYTES) throw new PsdError(`Decoded channel data of ${total} bytes is larger than the ${MAX_DECODED_BYTES}-byte limit.`);
|
|
8138
|
-
return total;
|
|
8139
|
-
}
|
|
8140
|
-
function packbitsBound(n) {
|
|
8141
|
-
return n + Math.ceil(n / 128);
|
|
8142
|
-
}
|
|
8143
|
-
function packbitsEncode(src, out, at) {
|
|
8144
|
-
const n = src.length;
|
|
8145
|
-
let i = 0;
|
|
8146
|
-
while (i < n) {
|
|
8147
|
-
let run = 1;
|
|
8148
|
-
while (i + run < n && run < 128 && src[i + run] === src[i]) run++;
|
|
8149
|
-
if (run >= 2) {
|
|
8150
|
-
out[at++] = 257 - run & 255;
|
|
8151
|
-
out[at++] = src[i];
|
|
8152
|
-
i += run;
|
|
8153
|
-
continue;
|
|
8154
|
-
}
|
|
8155
|
-
const start = i;
|
|
8156
|
-
let len = 0;
|
|
8157
|
-
while (i < n && len < 128) {
|
|
8158
|
-
if (i + 2 < n && src[i] === src[i + 1] && src[i] === src[i + 2]) break;
|
|
8159
|
-
i++;
|
|
8160
|
-
len++;
|
|
8161
|
-
}
|
|
8162
|
-
out[at++] = len - 1;
|
|
8163
|
-
out.set(src.subarray(start, start + len), at);
|
|
8164
|
-
at += len;
|
|
8165
|
-
}
|
|
8166
|
-
return at;
|
|
8167
|
-
}
|
|
8168
|
-
function packbitsDecodeInto(src, expected, out, at) {
|
|
8169
|
-
const target = at + expected;
|
|
8170
|
-
let i = 0;
|
|
8171
|
-
while (at < target) {
|
|
8172
|
-
if (i >= src.length) throw new PsdError("PackBits row ended early.");
|
|
8173
|
-
const h = src[i++];
|
|
8174
|
-
if (h < 128) {
|
|
8175
|
-
const len = h + 1;
|
|
8176
|
-
if (i + len > src.length) throw new PsdError("PackBits literal is truncated.");
|
|
8177
|
-
if (at + len > target) throw new PsdError("PackBits literal overflows its row.");
|
|
8178
|
-
out.set(src.subarray(i, i + len), at);
|
|
8179
|
-
at += len;
|
|
8180
|
-
i += len;
|
|
8181
|
-
} else if (h !== 128) {
|
|
8182
|
-
const len = 257 - h;
|
|
8183
|
-
if (i >= src.length) throw new PsdError("PackBits run is truncated.");
|
|
8184
|
-
const b = src[i++];
|
|
8185
|
-
if (at + len > target) throw new PsdError("PackBits run overflows its row.");
|
|
8186
|
-
out.fill(b, at, at + len);
|
|
8187
|
-
at += len;
|
|
8188
|
-
}
|
|
8189
|
-
}
|
|
8190
|
-
return at;
|
|
8191
|
-
}
|
|
8192
|
-
function inflateChecked(data, expected) {
|
|
8193
|
-
let out;
|
|
8194
|
-
try {
|
|
8195
|
-
out = inflate(data, { maxOutput: expected + (1 << 20), sizeHint: expected, verify: false });
|
|
8196
|
-
} catch (e) {
|
|
8197
|
-
throw new PsdError(`ZIP channel data: ${e instanceof ZlibError || e instanceof Error ? e.message : String(e)}`);
|
|
8198
|
-
}
|
|
8199
|
-
if (out.length < expected) throw new PsdError(`ZIP stream produced ${out.length} of ${expected} bytes.`);
|
|
8200
|
-
return out.length === expected ? out : out.subarray(0, expected);
|
|
8201
|
-
}
|
|
8202
|
-
function unpredict(buf, l) {
|
|
8203
|
-
const rb = rowBytes(l.width, l.depth);
|
|
8204
|
-
if (rb === 0) return;
|
|
8205
|
-
const w = l.width;
|
|
8206
|
-
const scratch = l.depth === 32 ? new Uint8Array(rb) : null;
|
|
8207
|
-
for (let o = 0; o + rb <= buf.length; o += rb) {
|
|
8208
|
-
if (l.depth === 1 || l.depth === 8) {
|
|
8209
|
-
for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
|
|
8210
|
-
} else if (l.depth === 16) {
|
|
8211
|
-
for (let i = 1; i < w; i++) {
|
|
8212
|
-
const a = buf[o + 2 * i] << 8 | buf[o + 2 * i + 1];
|
|
8213
|
-
const b = buf[o + 2 * i - 2] << 8 | buf[o + 2 * i - 1];
|
|
8214
|
-
const v = a + b & 65535;
|
|
8215
|
-
buf[o + 2 * i] = v >> 8;
|
|
8216
|
-
buf[o + 2 * i + 1] = v & 255;
|
|
8217
|
-
}
|
|
8218
|
-
} else if (l.depth === 32) {
|
|
8219
|
-
for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
|
|
8220
|
-
scratch.set(buf.subarray(o, o + rb));
|
|
8221
|
-
for (let i = 0; i < w; i++) for (let k = 0; k < 4; k++) buf[o + 4 * i + k] = scratch[k * w + i];
|
|
8222
|
-
} else {
|
|
8223
|
-
throw new PsdError(`ZIP prediction is not defined for depth ${l.depth}.`);
|
|
8224
|
-
}
|
|
8225
|
-
}
|
|
8226
|
-
}
|
|
8227
|
-
function decodeRle(r, l, total) {
|
|
8228
|
-
const rows = l.planes * l.height;
|
|
8229
|
-
const rb = rowBytes(l.width, l.depth);
|
|
8230
|
-
r.checkCount(rows, l.psb ? 4 : 2, "RLE row counts");
|
|
8231
|
-
const counts = new Array(rows);
|
|
8232
|
-
for (let i = 0; i < rows; i++) counts[i] = l.psb ? r.u32() : r.u16();
|
|
8233
|
-
if (total > r.remaining * 130) throw new PsdError("RLE data is too short for its declared size.");
|
|
8234
|
-
const out = new Uint8Array(total);
|
|
8235
|
-
let at = 0;
|
|
8236
|
-
for (const c of counts) at = packbitsDecodeInto(r.bytes(c), rb, out, at);
|
|
8237
|
-
return out;
|
|
8238
|
-
}
|
|
8239
|
-
function decodePlanes(compression, data, l) {
|
|
8240
|
-
const total = decodedLength(l);
|
|
8241
|
-
switch (compression) {
|
|
8242
|
-
case 0:
|
|
8243
|
-
if (data.length < total) throw new PsdError(`Raw channel data has ${data.length} of ${total} bytes.`);
|
|
8244
|
-
return data.slice(0, total);
|
|
8245
|
-
case 1:
|
|
8246
|
-
return decodeRle(new PsdReader(data), l, total);
|
|
8247
|
-
case 2:
|
|
8248
|
-
return inflateChecked(data, total).slice();
|
|
8249
|
-
case 3: {
|
|
8250
|
-
const v = inflateChecked(data, total).slice();
|
|
8251
|
-
unpredict(v, l);
|
|
8252
|
-
return v;
|
|
8253
|
-
}
|
|
8254
|
-
default:
|
|
8255
|
-
throw new PsdError(`Unsupported compression method ${compression}.`);
|
|
8256
|
-
}
|
|
8257
|
-
}
|
|
8258
|
-
function encodeRle(decoded2, l) {
|
|
8259
|
-
const rows = l.planes * l.height;
|
|
8260
|
-
const rb = rowBytes(l.width, l.depth);
|
|
8261
|
-
const cs = l.psb ? 4 : 2;
|
|
8262
|
-
const packed = new Uint8Array(rows * packbitsBound(rb));
|
|
8263
|
-
let at = 0;
|
|
8264
|
-
const counts = new Uint8Array(rows * cs);
|
|
8265
|
-
for (let row = 0; row < rows; row++) {
|
|
8266
|
-
const start = at;
|
|
8267
|
-
at = packbitsEncode(decoded2.subarray(row * rb, (row + 1) * rb), packed, at);
|
|
8268
|
-
const n = at - start;
|
|
8269
|
-
if (cs === 2) {
|
|
8270
|
-
if (n > 65535) throw new PsdError("An RLE row is longer than 65535 bytes; write a PSB instead.");
|
|
8271
|
-
counts[row * 2] = n >> 8;
|
|
8272
|
-
counts[row * 2 + 1] = n & 255;
|
|
8273
|
-
} else {
|
|
8274
|
-
counts[row * 4] = n >>> 24;
|
|
8275
|
-
counts[row * 4 + 1] = n >>> 16 & 255;
|
|
8276
|
-
counts[row * 4 + 2] = n >>> 8 & 255;
|
|
8277
|
-
counts[row * 4 + 3] = n & 255;
|
|
8278
|
-
}
|
|
8279
|
-
}
|
|
8280
|
-
const out = new Uint8Array(counts.length + at);
|
|
8281
|
-
out.set(counts);
|
|
8282
|
-
out.set(packed.subarray(0, at), counts.length);
|
|
8283
|
-
return out;
|
|
8284
|
-
}
|
|
8285
|
-
function predict(buf, l) {
|
|
8286
|
-
const rb = rowBytes(l.width, l.depth);
|
|
8287
|
-
if (rb === 0) return;
|
|
8288
|
-
const w = l.width;
|
|
8289
|
-
const scratch = l.depth === 32 ? new Uint8Array(rb) : null;
|
|
8290
|
-
for (let o = 0; o + rb <= buf.length; o += rb) {
|
|
8291
|
-
if (l.depth === 1 || l.depth === 8) {
|
|
8292
|
-
for (let i = rb - 1; i > 0; i--) buf[o + i] = buf[o + i] - buf[o + i - 1] & 255;
|
|
8293
|
-
} else if (l.depth === 16) {
|
|
8294
|
-
for (let i = w - 1; i > 0; i--) {
|
|
8295
|
-
const a = buf[o + 2 * i] << 8 | buf[o + 2 * i + 1];
|
|
8296
|
-
const b = buf[o + 2 * i - 2] << 8 | buf[o + 2 * i - 1];
|
|
8297
|
-
const v = a - b & 65535;
|
|
8298
|
-
buf[o + 2 * i] = v >> 8;
|
|
8299
|
-
buf[o + 2 * i + 1] = v & 255;
|
|
8300
|
-
}
|
|
8301
|
-
} else if (l.depth === 32) {
|
|
8302
|
-
scratch.set(buf.subarray(o, o + rb));
|
|
8303
|
-
for (let i = 0; i < w; i++) for (let k = 0; k < 4; k++) buf[o + k * w + i] = scratch[4 * i + k];
|
|
8304
|
-
for (let i = rb - 1; i > 0; i--) buf[o + i] = buf[o + i] - buf[o + i - 1] & 255;
|
|
8305
|
-
} else {
|
|
8306
|
-
throw new PsdError(`ZIP prediction is not defined for depth ${l.depth}.`);
|
|
8307
|
-
}
|
|
8308
|
-
}
|
|
8309
|
-
}
|
|
8310
|
-
function encodeZipPredicted(decoded2, l) {
|
|
8311
|
-
const buf = decoded2.slice();
|
|
8312
|
-
predict(buf, l);
|
|
8313
|
-
return deflate(buf);
|
|
8314
|
-
}
|
|
8315
|
-
|
|
8316
8409
|
// src/image-editor/psd/smart.ts
|
|
8317
8410
|
var dbl2 = (value) => ({ type: "doub", value });
|
|
8318
8411
|
var unit2 = (u, value) => ({ type: "UntF", unit: u, value });
|
|
@@ -10359,6 +10452,16 @@ function psdToLayers(file, options = {}) {
|
|
|
10359
10452
|
const bg = mergedLayer(ctx);
|
|
10360
10453
|
if (bg) layers = [bg];
|
|
10361
10454
|
}
|
|
10455
|
+
let alphaChannels = [];
|
|
10456
|
+
const hasTransparency = header.channels > ctx.colorCount && (file.layers.length === 0 || file.mergedAlpha);
|
|
10457
|
+
const firstAlpha = ctx.colorCount + (hasTransparency ? 1 : 0);
|
|
10458
|
+
if (!ctx.skipPixels && header.channels > firstAlpha) {
|
|
10459
|
+
try {
|
|
10460
|
+
alphaChannels = readAlphaChannels(decodeMergedImage(file), header, firstAlpha, (id) => resource(file, id), (m) => warn.extra.push(m));
|
|
10461
|
+
} catch (e) {
|
|
10462
|
+
warn.extra.push(`The alpha channels could not be read (${e instanceof Error ? e.message : String(e)}).`);
|
|
10463
|
+
}
|
|
10464
|
+
}
|
|
10362
10465
|
const i32 = (b) => b && b.length >= 4 ? new DataView(b.buffer, b.byteOffset, 4).getInt32(0) : void 0;
|
|
10363
10466
|
const angle = i32(resource(file, 1037));
|
|
10364
10467
|
const altitude = i32(resource(file, 1049));
|
|
@@ -10372,6 +10475,7 @@ function psdToLayers(file, options = {}) {
|
|
|
10372
10475
|
patterns: [...ctx.patterns.values()],
|
|
10373
10476
|
...angle !== void 0 || altitude !== void 0 ? { globalLight: { angle: Math.max(-360, Math.min(360, angle ?? 120)), altitude: Math.max(0, Math.min(90, altitude ?? 30)) } } : {},
|
|
10374
10477
|
info: { psb: header.psb, depth: header.depth, colorMode: mode, channels: header.channels },
|
|
10478
|
+
...alphaChannels.length ? { alphaChannels } : {},
|
|
10375
10479
|
...cmykProfile ? { sourceMode: { mode: "cmyk", profile: cmykProfile } } : header.colorMode === MODE_LAB ? { sourceMode: { mode: "lab" } } : {},
|
|
10376
10480
|
...resource(file, 1039) ? { iccProfile: resource(file, 1039).slice() } : {},
|
|
10377
10481
|
bitDepth: (header.depth === 16 || header.depth === 32) && header.colorMode !== MODE_INDEXED && header.colorMode !== MODE_BITMAP ? header.depth : 8
|
|
@@ -11409,6 +11513,10 @@ function exportPsd(input) {
|
|
|
11409
11513
|
planes = transparent ? dp : dp.slice(0, 3);
|
|
11410
11514
|
}
|
|
11411
11515
|
}
|
|
11516
|
+
for (const ch of input.alphaChannels ?? []) {
|
|
11517
|
+
if (ch.data.length !== n) throw new PsdError(`Alpha channel "${ch.name}" has ${ch.data.length} values, expected ${n}.`);
|
|
11518
|
+
planes.push(planeAtDepth(ch.data, depth));
|
|
11519
|
+
}
|
|
11412
11520
|
const planeBytes = n * (depth >> 3);
|
|
11413
11521
|
const merged2 = new Uint8Array(planeBytes * planes.length);
|
|
11414
11522
|
planes.forEach((p, i) => merged2.set(p, i * planeBytes));
|
|
@@ -11430,6 +11538,7 @@ function exportPsd(input) {
|
|
|
11430
11538
|
{ id: 1057, name: "", data: version.result() }
|
|
11431
11539
|
];
|
|
11432
11540
|
if (input.iccProfile?.length) resources.push({ id: 1039, name: "", data: input.iccProfile });
|
|
11541
|
+
resources.push(...alphaChannelResources(input.alphaChannels ?? []));
|
|
11433
11542
|
if (input.globalLight) {
|
|
11434
11543
|
const i32 = (v) => {
|
|
11435
11544
|
const w = new PsdWriter();
|
|
@@ -11818,6 +11927,8 @@ export {
|
|
|
11818
11927
|
deserializeLayer,
|
|
11819
11928
|
deserializeLayers,
|
|
11820
11929
|
PsdError,
|
|
11930
|
+
decodePlanes,
|
|
11931
|
+
encodeRle,
|
|
11821
11932
|
parseVersionedDescriptor,
|
|
11822
11933
|
versionedDescriptorBytes,
|
|
11823
11934
|
ADJUSTMENT_KEYS,
|
|
@@ -11827,8 +11938,6 @@ export {
|
|
|
11827
11938
|
parseLfx2,
|
|
11828
11939
|
writeLfx2,
|
|
11829
11940
|
parseLrfx,
|
|
11830
|
-
decodePlanes,
|
|
11831
|
-
encodeRle,
|
|
11832
11941
|
BROWSER_IMAGE_TYPES,
|
|
11833
11942
|
readPsd,
|
|
11834
11943
|
layerName,
|