@pixerate/editor 0.103.0 → 0.105.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 +2 -0
- package/dist/{chunk-NQLQ2JOI.js → chunk-4X5MIVCE.js} +1155 -295
- package/dist/chunk-4X5MIVCE.js.map +1 -0
- package/dist/{chunk-VSMM7B3P.cjs → chunk-ZID226ZG.cjs} +1169 -309
- package/dist/chunk-ZID226ZG.cjs.map +1 -0
- package/dist/{compositor-CTMU-B1t.d.cts → compositor-Bc3PNP3C.d.cts} +1 -1
- package/dist/{compositor-B1ZrKR89.d.ts → compositor-BsYYllvR.d.ts} +1 -1
- package/dist/{export-X9rY79p0.d.cts → export-_m3Es4ru.d.cts} +19 -4
- package/dist/{export-BLpMyCYg.d.ts → export-iMrop3Jm.d.ts} +19 -4
- package/dist/image-editor/index.cjs +523 -539
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +16 -8
- package/dist/image-editor/index.d.ts +16 -8
- package/dist/image-editor/index.js +37 -53
- package/dist/image-editor/index.js.map +1 -1
- package/dist/{mixer-DGcH6HLu.d.cts → mixer-C1M1Nua_.d.cts} +1 -1
- package/dist/{mixer-DG_9Yu2W.d.ts → mixer-Cu5a9Bzd.d.ts} +1 -1
- package/dist/paint/index.d.cts +2 -2
- package/dist/paint/index.d.ts +2 -2
- package/dist/paint-editor/index.cjs +1531 -128
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +461 -14
- package/dist/paint-editor/index.d.ts +461 -14
- package/dist/paint-editor/index.js +1479 -76
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/raster/index.d.cts +4 -4
- package/dist/raster/index.d.ts +4 -4
- package/dist/{surface-AfHCH7BR.d.cts → surface-BBJN9wxg.d.cts} +1 -1
- package/dist/{surface-AfHCH7BR.d.ts → surface-BBJN9wxg.d.ts} +1 -1
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +14 -5
- package/skills/pixerate-paint-editor/SKILL.md +49 -8
- package/dist/chunk-NQLQ2JOI.js.map +0 -1
- package/dist/chunk-VSMM7B3P.cjs.map +0 -1
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
effectsFingerprint,
|
|
17
17
|
effectsMargin,
|
|
18
18
|
effectsScale,
|
|
19
|
+
encodePng,
|
|
19
20
|
findLayer,
|
|
20
21
|
flattenLayers,
|
|
21
22
|
gaussianBlur,
|
|
@@ -60,8 +61,10 @@ import {
|
|
|
60
61
|
translateSurface
|
|
61
62
|
} from "./chunk-HEW6KDNI.js";
|
|
62
63
|
import {
|
|
64
|
+
WARP_STYLES,
|
|
63
65
|
applyMatrix,
|
|
64
66
|
evalWarpMesh,
|
|
67
|
+
fitWarpMesh,
|
|
65
68
|
identityWarpMesh,
|
|
66
69
|
invertMatrix,
|
|
67
70
|
isValidWarpMesh,
|
|
@@ -855,21 +858,21 @@ function toBase64(bytes, length) {
|
|
|
855
858
|
parts.push(chunk);
|
|
856
859
|
return parts.join("");
|
|
857
860
|
}
|
|
858
|
-
function fromBase64(
|
|
859
|
-
if (
|
|
861
|
+
function fromBase64(text3) {
|
|
862
|
+
if (text3.length % 4 !== 0) throw new Error("Invalid base64 length");
|
|
860
863
|
let pad = 0;
|
|
861
|
-
if (
|
|
862
|
-
else if (
|
|
863
|
-
const out = new Uint8Array(
|
|
864
|
+
if (text3.endsWith("==")) pad = 2;
|
|
865
|
+
else if (text3.endsWith("=")) pad = 1;
|
|
866
|
+
const out = new Uint8Array(text3.length / 4 * 3 - pad);
|
|
864
867
|
let o = 0;
|
|
865
868
|
const digit = (i, k) => {
|
|
866
|
-
const ch =
|
|
867
|
-
if (ch === 61 && i + 4 ===
|
|
869
|
+
const ch = text3.charCodeAt(i + k);
|
|
870
|
+
if (ch === 61 && i + 4 === text3.length && k >= 4 - pad) return 0;
|
|
868
871
|
const idx = ch < 128 ? B64_INDEX[ch] : -1;
|
|
869
872
|
if (idx < 0) throw new Error("Invalid base64 character");
|
|
870
873
|
return idx;
|
|
871
874
|
};
|
|
872
|
-
for (let i = 0; i <
|
|
875
|
+
for (let i = 0; i < text3.length; i += 4) {
|
|
873
876
|
const n = digit(i, 0) << 18 | digit(i, 1) << 12 | digit(i, 2) << 6 | digit(i, 3);
|
|
874
877
|
if (o < out.length) out[o++] = n >> 16 & 255;
|
|
875
878
|
if (o < out.length) out[o++] = n >> 8 & 255;
|
|
@@ -4030,8 +4033,8 @@ var PsdReader = class _PsdReader {
|
|
|
4030
4033
|
return v;
|
|
4031
4034
|
}
|
|
4032
4035
|
/** A length field: 8 bytes in PSB where the format says so, else 4. */
|
|
4033
|
-
len(
|
|
4034
|
-
return
|
|
4036
|
+
len(long4) {
|
|
4037
|
+
return long4 ? this.u64() : this.u32();
|
|
4035
4038
|
}
|
|
4036
4039
|
bytes(n) {
|
|
4037
4040
|
this.need(n, `${n} bytes`);
|
|
@@ -4138,11 +4141,11 @@ var PsdWriter = class {
|
|
|
4138
4141
|
this.length += n;
|
|
4139
4142
|
}
|
|
4140
4143
|
/** Reserves a length field; `endLen` fills it with the bytes written since. */
|
|
4141
|
-
beginLen(
|
|
4144
|
+
beginLen(long4) {
|
|
4142
4145
|
const at = this.length;
|
|
4143
|
-
if (
|
|
4146
|
+
if (long4) this.u64(0);
|
|
4144
4147
|
else this.u32(0);
|
|
4145
|
-
return { at, long:
|
|
4148
|
+
return { at, long: long4 };
|
|
4146
4149
|
}
|
|
4147
4150
|
endLen(mark) {
|
|
4148
4151
|
const n = this.length - mark.at - (mark.long ? 8 : 4);
|
|
@@ -4187,6 +4190,162 @@ function encodeLegacyName(s) {
|
|
|
4187
4190
|
return Uint8Array.from(out);
|
|
4188
4191
|
}
|
|
4189
4192
|
|
|
4193
|
+
// src/image-editor/psd/compression.ts
|
|
4194
|
+
var MAX_DECODED_BYTES = 2 ** 31;
|
|
4195
|
+
function rowBytes(width, depth) {
|
|
4196
|
+
return depth === 1 ? Math.ceil(width / 8) : width * Math.max(1, depth >> 3);
|
|
4197
|
+
}
|
|
4198
|
+
function decodedLength(l) {
|
|
4199
|
+
const total = l.planes * l.height * rowBytes(l.width, l.depth);
|
|
4200
|
+
if (total > MAX_DECODED_BYTES) throw new PsdError(`Decoded channel data of ${total} bytes is larger than the ${MAX_DECODED_BYTES}-byte limit.`);
|
|
4201
|
+
return total;
|
|
4202
|
+
}
|
|
4203
|
+
function packbitsBound(n) {
|
|
4204
|
+
return n + Math.ceil(n / 128);
|
|
4205
|
+
}
|
|
4206
|
+
function packbitsEncode(src, out, at) {
|
|
4207
|
+
const n = src.length;
|
|
4208
|
+
let i = 0;
|
|
4209
|
+
while (i < n) {
|
|
4210
|
+
let run = 1;
|
|
4211
|
+
while (i + run < n && run < 128 && src[i + run] === src[i]) run++;
|
|
4212
|
+
if (run >= 2) {
|
|
4213
|
+
out[at++] = 257 - run & 255;
|
|
4214
|
+
out[at++] = src[i];
|
|
4215
|
+
i += run;
|
|
4216
|
+
continue;
|
|
4217
|
+
}
|
|
4218
|
+
const start = i;
|
|
4219
|
+
let len = 0;
|
|
4220
|
+
while (i < n && len < 128) {
|
|
4221
|
+
if (i + 2 < n && src[i] === src[i + 1] && src[i] === src[i + 2]) break;
|
|
4222
|
+
i++;
|
|
4223
|
+
len++;
|
|
4224
|
+
}
|
|
4225
|
+
out[at++] = len - 1;
|
|
4226
|
+
out.set(src.subarray(start, start + len), at);
|
|
4227
|
+
at += len;
|
|
4228
|
+
}
|
|
4229
|
+
return at;
|
|
4230
|
+
}
|
|
4231
|
+
function packbitsDecodeInto(src, expected, out, at) {
|
|
4232
|
+
const target = at + expected;
|
|
4233
|
+
let i = 0;
|
|
4234
|
+
while (at < target) {
|
|
4235
|
+
if (i >= src.length) throw new PsdError("PackBits row ended early.");
|
|
4236
|
+
const h = src[i++];
|
|
4237
|
+
if (h < 128) {
|
|
4238
|
+
const len = h + 1;
|
|
4239
|
+
if (i + len > src.length) throw new PsdError("PackBits literal is truncated.");
|
|
4240
|
+
if (at + len > target) throw new PsdError("PackBits literal overflows its row.");
|
|
4241
|
+
out.set(src.subarray(i, i + len), at);
|
|
4242
|
+
at += len;
|
|
4243
|
+
i += len;
|
|
4244
|
+
} else if (h !== 128) {
|
|
4245
|
+
const len = 257 - h;
|
|
4246
|
+
if (i >= src.length) throw new PsdError("PackBits run is truncated.");
|
|
4247
|
+
const b = src[i++];
|
|
4248
|
+
if (at + len > target) throw new PsdError("PackBits run overflows its row.");
|
|
4249
|
+
out.fill(b, at, at + len);
|
|
4250
|
+
at += len;
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
return at;
|
|
4254
|
+
}
|
|
4255
|
+
function inflateChecked(data, expected) {
|
|
4256
|
+
let out;
|
|
4257
|
+
try {
|
|
4258
|
+
out = inflate(data, { maxOutput: expected + (1 << 20), sizeHint: expected, verify: false });
|
|
4259
|
+
} catch (e) {
|
|
4260
|
+
throw new PsdError(`ZIP channel data: ${e instanceof ZlibError || e instanceof Error ? e.message : String(e)}`);
|
|
4261
|
+
}
|
|
4262
|
+
if (out.length < expected) throw new PsdError(`ZIP stream produced ${out.length} of ${expected} bytes.`);
|
|
4263
|
+
return out.length === expected ? out : out.subarray(0, expected);
|
|
4264
|
+
}
|
|
4265
|
+
function unpredict(buf, l) {
|
|
4266
|
+
const rb = rowBytes(l.width, l.depth);
|
|
4267
|
+
if (rb === 0) return;
|
|
4268
|
+
const w = l.width;
|
|
4269
|
+
const scratch = l.depth === 32 ? new Uint8Array(rb) : null;
|
|
4270
|
+
for (let o = 0; o + rb <= buf.length; o += rb) {
|
|
4271
|
+
if (l.depth === 1 || l.depth === 8) {
|
|
4272
|
+
for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
|
|
4273
|
+
} else if (l.depth === 16) {
|
|
4274
|
+
for (let i = 1; i < w; i++) {
|
|
4275
|
+
const a = buf[o + 2 * i] << 8 | buf[o + 2 * i + 1];
|
|
4276
|
+
const b = buf[o + 2 * i - 2] << 8 | buf[o + 2 * i - 1];
|
|
4277
|
+
const v = a + b & 65535;
|
|
4278
|
+
buf[o + 2 * i] = v >> 8;
|
|
4279
|
+
buf[o + 2 * i + 1] = v & 255;
|
|
4280
|
+
}
|
|
4281
|
+
} else if (l.depth === 32) {
|
|
4282
|
+
for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
|
|
4283
|
+
scratch.set(buf.subarray(o, o + rb));
|
|
4284
|
+
for (let i = 0; i < w; i++) for (let k = 0; k < 4; k++) buf[o + 4 * i + k] = scratch[k * w + i];
|
|
4285
|
+
} else {
|
|
4286
|
+
throw new PsdError(`ZIP prediction is not defined for depth ${l.depth}.`);
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
function decodeRle(r, l, total) {
|
|
4291
|
+
const rows = l.planes * l.height;
|
|
4292
|
+
const rb = rowBytes(l.width, l.depth);
|
|
4293
|
+
r.checkCount(rows, l.psb ? 4 : 2, "RLE row counts");
|
|
4294
|
+
const counts = new Array(rows);
|
|
4295
|
+
for (let i = 0; i < rows; i++) counts[i] = l.psb ? r.u32() : r.u16();
|
|
4296
|
+
if (total > r.remaining * 130) throw new PsdError("RLE data is too short for its declared size.");
|
|
4297
|
+
const out = new Uint8Array(total);
|
|
4298
|
+
let at = 0;
|
|
4299
|
+
for (const c of counts) at = packbitsDecodeInto(r.bytes(c), rb, out, at);
|
|
4300
|
+
return out;
|
|
4301
|
+
}
|
|
4302
|
+
function decodePlanes(compression, data, l) {
|
|
4303
|
+
const total = decodedLength(l);
|
|
4304
|
+
switch (compression) {
|
|
4305
|
+
case 0:
|
|
4306
|
+
if (data.length < total) throw new PsdError(`Raw channel data has ${data.length} of ${total} bytes.`);
|
|
4307
|
+
return data.slice(0, total);
|
|
4308
|
+
case 1:
|
|
4309
|
+
return decodeRle(new PsdReader(data), l, total);
|
|
4310
|
+
case 2:
|
|
4311
|
+
return inflateChecked(data, total).slice();
|
|
4312
|
+
case 3: {
|
|
4313
|
+
const v = inflateChecked(data, total).slice();
|
|
4314
|
+
unpredict(v, l);
|
|
4315
|
+
return v;
|
|
4316
|
+
}
|
|
4317
|
+
default:
|
|
4318
|
+
throw new PsdError(`Unsupported compression method ${compression}.`);
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
function encodeRle(decoded2, l) {
|
|
4322
|
+
const rows = l.planes * l.height;
|
|
4323
|
+
const rb = rowBytes(l.width, l.depth);
|
|
4324
|
+
const cs = l.psb ? 4 : 2;
|
|
4325
|
+
const packed = new Uint8Array(rows * packbitsBound(rb));
|
|
4326
|
+
let at = 0;
|
|
4327
|
+
const counts = new Uint8Array(rows * cs);
|
|
4328
|
+
for (let row = 0; row < rows; row++) {
|
|
4329
|
+
const start = at;
|
|
4330
|
+
at = packbitsEncode(decoded2.subarray(row * rb, (row + 1) * rb), packed, at);
|
|
4331
|
+
const n = at - start;
|
|
4332
|
+
if (cs === 2) {
|
|
4333
|
+
if (n > 65535) throw new PsdError("An RLE row is longer than 65535 bytes; write a PSB instead.");
|
|
4334
|
+
counts[row * 2] = n >> 8;
|
|
4335
|
+
counts[row * 2 + 1] = n & 255;
|
|
4336
|
+
} else {
|
|
4337
|
+
counts[row * 4] = n >>> 24;
|
|
4338
|
+
counts[row * 4 + 1] = n >>> 16 & 255;
|
|
4339
|
+
counts[row * 4 + 2] = n >>> 8 & 255;
|
|
4340
|
+
counts[row * 4 + 3] = n & 255;
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
const out = new Uint8Array(counts.length + at);
|
|
4344
|
+
out.set(counts);
|
|
4345
|
+
out.set(packed.subarray(0, at), counts.length);
|
|
4346
|
+
return out;
|
|
4347
|
+
}
|
|
4348
|
+
|
|
4190
4349
|
// src/image-editor/psd/descriptor.ts
|
|
4191
4350
|
var MAX_DEPTH = 64;
|
|
4192
4351
|
var STRING_IDS = /* @__PURE__ */ new Set(["warp", "list", "time"]);
|
|
@@ -4302,12 +4461,12 @@ function readValue(r, depth) {
|
|
|
4302
4461
|
case "UntF":
|
|
4303
4462
|
return { type, unit: r.ascii(4), value: r.f64() };
|
|
4304
4463
|
case "UnFl": {
|
|
4305
|
-
const
|
|
4464
|
+
const unit3 = r.ascii(4);
|
|
4306
4465
|
const n = r.u32();
|
|
4307
4466
|
r.checkCount(n, 8, "Unit float list");
|
|
4308
4467
|
const values = [];
|
|
4309
4468
|
for (let i = 0; i < n; i++) values.push(r.f64());
|
|
4310
|
-
return { type, unit:
|
|
4469
|
+
return { type, unit: unit3, values };
|
|
4311
4470
|
}
|
|
4312
4471
|
case "TEXT":
|
|
4313
4472
|
return { type, value: r.unicode() };
|
|
@@ -4760,11 +4919,11 @@ function writeAdjustmentBlock(kind, params) {
|
|
|
4760
4919
|
case "blackWhite": {
|
|
4761
4920
|
const p = params;
|
|
4762
4921
|
const tint = p.tint ? hexToRgb(p.tint) : BW_TINT;
|
|
4763
|
-
const
|
|
4922
|
+
const dbl3 = (v) => ({ type: "doub", value: clamp2(v, 0, 1) * 255 });
|
|
4764
4923
|
const items = BW_KEYS.map(([field, key]) => [key, long(clamp2(p[field], -200, 300))]);
|
|
4765
4924
|
items.push(
|
|
4766
4925
|
["useTint", { type: "bool", value: p.tint !== null }],
|
|
4767
|
-
["tintColor", { type: "Objc", value: descriptor("RGBC", [["Rd ",
|
|
4926
|
+
["tintColor", { type: "Objc", value: descriptor("RGBC", [["Rd ", dbl3(tint[0])], ["Grn ", dbl3(tint[1])], ["Bl ", dbl3(tint[2])]]) }],
|
|
4768
4927
|
["bwPresetKind", { type: "long", value: 1 }],
|
|
4769
4928
|
["blackAndWhitePresetFileName", { type: "TEXT", value: "" }]
|
|
4770
4929
|
);
|
|
@@ -4786,161 +4945,562 @@ function writeAdjustmentBlock(kind, params) {
|
|
|
4786
4945
|
throw new Error(`Unknown adjustment kind "${kind}".`);
|
|
4787
4946
|
}
|
|
4788
4947
|
|
|
4789
|
-
// src/image-editor/psd/
|
|
4790
|
-
var
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
}
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4948
|
+
// src/image-editor/psd/smart.ts
|
|
4949
|
+
var dbl = (value) => ({ type: "doub", value });
|
|
4950
|
+
var unit = (u, value) => ({ type: "UntF", unit: u, value });
|
|
4951
|
+
var long2 = (value) => ({ type: "long", value: Math.round(value) });
|
|
4952
|
+
var boolV = (value) => ({ type: "bool", value });
|
|
4953
|
+
var enumV = (typeId, value) => ({ type: "enum", typeId, value });
|
|
4954
|
+
var textV = (value) => ({ type: "TEXT", value });
|
|
4955
|
+
var obj = (d) => ({ type: "Objc", value: d });
|
|
4956
|
+
var text = (d, key) => {
|
|
4957
|
+
const v = descGet(d, key);
|
|
4958
|
+
return v?.type === "TEXT" ? v.value : void 0;
|
|
4959
|
+
};
|
|
4960
|
+
var enumOf = (d, key) => {
|
|
4961
|
+
const v = descGet(d, key);
|
|
4962
|
+
return v?.type === "enum" ? v.value : void 0;
|
|
4963
|
+
};
|
|
4964
|
+
var numbers = (d, key) => {
|
|
4965
|
+
const v = descGet(d, key);
|
|
4966
|
+
if (v?.type !== "VlLs") return null;
|
|
4967
|
+
const out = [];
|
|
4968
|
+
for (const it of v.items) {
|
|
4969
|
+
if (it.type === "doub" || it.type === "UntF" || it.type === "long") out.push(it.value);
|
|
4970
|
+
else return null;
|
|
4971
|
+
}
|
|
4972
|
+
return out;
|
|
4973
|
+
};
|
|
4974
|
+
function setItem(d, key, v) {
|
|
4975
|
+
const i = d.items.findIndex(([k]) => k === key);
|
|
4976
|
+
if (i >= 0) d.items[i] = [key, v];
|
|
4977
|
+
else d.items.push([key, v]);
|
|
4978
|
+
}
|
|
4979
|
+
function cloneDescriptor(d) {
|
|
4980
|
+
return parseVersionedDescriptor(versionedDescriptorBytes(d));
|
|
4981
|
+
}
|
|
4982
|
+
function psdUuid(seed) {
|
|
4983
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(seed)) return seed;
|
|
4984
|
+
let hex = "";
|
|
4985
|
+
for (let k = 0; k < 4; k++) {
|
|
4986
|
+
let h = (2166136261 ^ k * 2654435769) >>> 0;
|
|
4987
|
+
for (let i = 0; i < seed.length; i++) h = Math.imul(h ^ seed.charCodeAt(i), 16777619) >>> 0;
|
|
4988
|
+
hex += h.toString(16).padStart(8, "0");
|
|
4989
|
+
}
|
|
4990
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
4991
|
+
}
|
|
4992
|
+
var LINKED_FILE_KEYS = ["lnk2", "lnk3", "lnkD", "lnkE"];
|
|
4993
|
+
function readLinkedItem(item) {
|
|
4994
|
+
const r = new PsdReader(item);
|
|
4995
|
+
const kind = r.ascii(4);
|
|
4996
|
+
const version = r.u32();
|
|
4997
|
+
const n = r.u8();
|
|
4998
|
+
const uuid = new TextDecoder().decode(r.bytes(n));
|
|
4999
|
+
const name = r.unicode();
|
|
5000
|
+
const fileType = r.ascii(4);
|
|
5001
|
+
r.skip(4);
|
|
5002
|
+
const len = r.u64();
|
|
5003
|
+
if (r.u8() !== 0) readVersionedDescriptor(r);
|
|
5004
|
+
if (kind === "liFD") return { kind, uuid, name, fileType, bytes: r.bytes(Math.min(len, r.remaining)) };
|
|
5005
|
+
let path;
|
|
5006
|
+
if (kind === "liFE") {
|
|
5007
|
+
try {
|
|
5008
|
+
const d = readVersionedDescriptor(r);
|
|
5009
|
+
path = text(d, "fullPath") ?? text(d, "originalPath") ?? text(d, "relPath");
|
|
5010
|
+
} catch {
|
|
5011
|
+
path = void 0;
|
|
4820
5012
|
}
|
|
4821
|
-
|
|
4822
|
-
out.set(src.subarray(start, start + len), at);
|
|
4823
|
-
at += len;
|
|
5013
|
+
void version;
|
|
4824
5014
|
}
|
|
4825
|
-
return
|
|
5015
|
+
return { kind, uuid, name, fileType, bytes: new Uint8Array(0), path: path ?? name };
|
|
4826
5016
|
}
|
|
4827
|
-
function
|
|
4828
|
-
const
|
|
4829
|
-
let
|
|
4830
|
-
while (at
|
|
4831
|
-
|
|
4832
|
-
const
|
|
4833
|
-
if (
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
if (
|
|
4837
|
-
|
|
4838
|
-
at += len;
|
|
4839
|
-
i += len;
|
|
4840
|
-
} else if (h !== 128) {
|
|
4841
|
-
const len = 257 - h;
|
|
4842
|
-
if (i >= src.length) throw new PsdError("PackBits run is truncated.");
|
|
4843
|
-
const b = src[i++];
|
|
4844
|
-
if (at + len > target) throw new PsdError("PackBits run overflows its row.");
|
|
4845
|
-
out.fill(b, at, at + len);
|
|
4846
|
-
at += len;
|
|
5017
|
+
function parseLinkedFiles(data) {
|
|
5018
|
+
const out = [];
|
|
5019
|
+
let at = 0;
|
|
5020
|
+
while (at + 8 <= data.length) {
|
|
5021
|
+
const r = new PsdReader(data, at);
|
|
5022
|
+
const len = r.u64();
|
|
5023
|
+
if (!(len > 0) || at + 8 + len > data.length) break;
|
|
5024
|
+
try {
|
|
5025
|
+
const item = readLinkedItem(data.subarray(at + 8, at + 8 + len));
|
|
5026
|
+
if (item) out.push(item);
|
|
5027
|
+
} catch {
|
|
4847
5028
|
}
|
|
5029
|
+
at = at + 8 + len + 3 & ~3;
|
|
4848
5030
|
}
|
|
4849
|
-
return
|
|
5031
|
+
return out;
|
|
4850
5032
|
}
|
|
4851
|
-
function
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
}
|
|
4858
|
-
if (
|
|
4859
|
-
|
|
5033
|
+
function fileTypeOf(bytes) {
|
|
5034
|
+
const b = bytes;
|
|
5035
|
+
if (b[0] === 56 && b[1] === 66 && b[2] === 80 && b[3] === 83) return b[5] === 2 ? { code: "8BPB", ext: "psb" } : { code: "8BPS", ext: "psd" };
|
|
5036
|
+
if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) return { code: "PNGf", ext: "png" };
|
|
5037
|
+
if (b[0] === 255 && b[1] === 216) return { code: "JPEG", ext: "jpg" };
|
|
5038
|
+
if (b[0] === 73 && b[1] === 73 || b[0] === 77 && b[1] === 77) return { code: "TIFF", ext: "tif" };
|
|
5039
|
+
if (b[0] === 71 && b[1] === 73 && b[2] === 70) return { code: "GIFf", ext: "gif" };
|
|
5040
|
+
if (b[0] === 37 && b[1] === 80 && b[2] === 68 && b[3] === 70) return { code: "PDF ", ext: "pdf" };
|
|
5041
|
+
const head = new TextDecoder().decode(b.subarray(0, 256)).trimStart();
|
|
5042
|
+
if (head.startsWith("<?xml") || head.startsWith("<svg")) return { code: "SVG ", ext: "svg" };
|
|
5043
|
+
return { code: " ", ext: "bin" };
|
|
5044
|
+
}
|
|
5045
|
+
function encodeLinkedFile(f) {
|
|
5046
|
+
const w = new PsdWriter();
|
|
5047
|
+
w.ascii("liFD");
|
|
5048
|
+
w.u32(7);
|
|
5049
|
+
const id = new TextEncoder().encode(f.uuid).subarray(0, 255);
|
|
5050
|
+
w.u8(id.length);
|
|
5051
|
+
w.bytes(id);
|
|
5052
|
+
w.unicode(f.name, true);
|
|
5053
|
+
w.ascii(fileTypeOf(f.bytes).code);
|
|
5054
|
+
w.ascii("8BIM");
|
|
5055
|
+
w.u64(f.bytes.length);
|
|
5056
|
+
w.u8(0);
|
|
5057
|
+
w.bytes(f.bytes);
|
|
5058
|
+
w.u32(0);
|
|
5059
|
+
w.f64(0);
|
|
5060
|
+
w.u8(0);
|
|
5061
|
+
const item = w.result();
|
|
5062
|
+
const out = new PsdWriter();
|
|
5063
|
+
out.u64(item.length);
|
|
5064
|
+
out.bytes(item);
|
|
5065
|
+
out.zeros((4 - out.length % 4) % 4);
|
|
5066
|
+
return out.result();
|
|
4860
5067
|
}
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
const
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
5068
|
+
var FILTER_EFFECTS_KEYS = ["FEid", "FXid"];
|
|
5069
|
+
var readRect = (r) => ({ top: r.i32(), left: r.i32(), bottom: r.i32(), right: r.i32() });
|
|
5070
|
+
function readPlane(r) {
|
|
5071
|
+
const len = r.u64();
|
|
5072
|
+
if (len < 2) throw new PsdError("Filter effects: a plane is shorter than its compression field.");
|
|
5073
|
+
const s = r.sub(len);
|
|
5074
|
+
return { compression: s.u16(), data: s.rest() };
|
|
5075
|
+
}
|
|
5076
|
+
function parseFilterEffects(data) {
|
|
5077
|
+
const r = new PsdReader(data);
|
|
5078
|
+
const version = r.u32();
|
|
5079
|
+
if (version < 1 || version > 3) throw new PsdError(`Filter effects version ${version}.`);
|
|
5080
|
+
const items = [];
|
|
5081
|
+
while (r.remaining >= 8) {
|
|
5082
|
+
const len = r.u64();
|
|
5083
|
+
const body = r.sub(len);
|
|
5084
|
+
while (body.remaining > 0) {
|
|
5085
|
+
const id = new TextDecoder().decode(body.pascal(1));
|
|
5086
|
+
const itemVersion = body.u32();
|
|
5087
|
+
const it = body.sub(body.u64());
|
|
5088
|
+
const rect = readRect(it);
|
|
5089
|
+
const depth = it.u32();
|
|
5090
|
+
const maxChannels = it.u32();
|
|
5091
|
+
if (maxChannels > 64) throw new PsdError("Filter effects: too many channels.");
|
|
5092
|
+
const slots = [];
|
|
5093
|
+
for (let i = 0; i < maxChannels + 2; i++) slots.push(it.u32() !== 0 ? readPlane(it) : null);
|
|
5094
|
+
let mask = null;
|
|
5095
|
+
if (body.remaining > 0 && body.u8() !== 0) mask = { rect: readRect(body), plane: readPlane(body) };
|
|
5096
|
+
items.push({ id, version: itemVersion, rect, depth, maxChannels, slots, mask });
|
|
5097
|
+
}
|
|
5098
|
+
const pad = (4 - len % 4) % 4;
|
|
5099
|
+
r.skip(Math.min(pad, r.remaining));
|
|
5100
|
+
if (r.remaining < 8) break;
|
|
5101
|
+
}
|
|
5102
|
+
return { version, items };
|
|
5103
|
+
}
|
|
5104
|
+
function writeFilterEffects(items, version = 3) {
|
|
5105
|
+
const w = new PsdWriter();
|
|
5106
|
+
w.u32(version);
|
|
5107
|
+
const plane = (b, p) => {
|
|
5108
|
+
b.u64(p.data.length + 2);
|
|
5109
|
+
b.u16(p.compression);
|
|
5110
|
+
b.bytes(p.data);
|
|
5111
|
+
};
|
|
5112
|
+
const rect = (b, r) => {
|
|
5113
|
+
b.i32(r.top);
|
|
5114
|
+
b.i32(r.left);
|
|
5115
|
+
b.i32(r.bottom);
|
|
5116
|
+
b.i32(r.right);
|
|
5117
|
+
};
|
|
5118
|
+
for (const it of items) {
|
|
5119
|
+
const b = new PsdWriter();
|
|
5120
|
+
b.pascal(new TextEncoder().encode(it.id), 1);
|
|
5121
|
+
b.u32(it.version);
|
|
5122
|
+
const mark = b.beginLen(true);
|
|
5123
|
+
rect(b, it.rect);
|
|
5124
|
+
b.u32(it.depth);
|
|
5125
|
+
b.u32(it.maxChannels);
|
|
5126
|
+
for (let i = 0; i < it.maxChannels + 2; i++) {
|
|
5127
|
+
const p = it.slots[i];
|
|
5128
|
+
if (p) {
|
|
5129
|
+
b.u32(1);
|
|
5130
|
+
plane(b, p);
|
|
5131
|
+
} else b.u32(0);
|
|
5132
|
+
}
|
|
5133
|
+
b.endLen(mark);
|
|
5134
|
+
if (it.mask) {
|
|
5135
|
+
b.u8(1);
|
|
5136
|
+
rect(b, it.mask.rect);
|
|
5137
|
+
plane(b, it.mask.plane);
|
|
5138
|
+
} else b.u8(0);
|
|
5139
|
+
const body = b.result();
|
|
5140
|
+
w.u64(body.length);
|
|
5141
|
+
w.bytes(body);
|
|
5142
|
+
w.zeros((4 - w.length % 4) % 4);
|
|
5143
|
+
}
|
|
5144
|
+
if (!items.length) w.u64(0);
|
|
5145
|
+
return w.result();
|
|
5146
|
+
}
|
|
5147
|
+
function filterMaskFromItem(item) {
|
|
5148
|
+
if (!item.mask || item.depth !== 8) return null;
|
|
5149
|
+
const { rect, plane } = item.mask;
|
|
5150
|
+
const width = rect.right - rect.left;
|
|
5151
|
+
const height = rect.bottom - rect.top;
|
|
5152
|
+
if (!(width > 0 && height > 0) || width * height > 1 << 28) return null;
|
|
5153
|
+
const bytes = decodePlanes(plane.compression, plane.data, { planes: 1, width, height, depth: 8, psb: true });
|
|
5154
|
+
const surface = surfaceFromPixels({ width, height, data: bytes }, 1, rect.left, rect.top, 255);
|
|
5155
|
+
return { surface, defaultValue: 255 };
|
|
5156
|
+
}
|
|
5157
|
+
function filterMaskItem(placed, mask, area) {
|
|
5158
|
+
const painted = contentBounds(mask.surface);
|
|
5159
|
+
let b = painted ?? { x: 0, y: 0, width: 1, height: 1 };
|
|
5160
|
+
if (mask.defaultValue !== 255 && area) {
|
|
5161
|
+
const x0 = Math.min(area.x, painted?.x ?? area.x);
|
|
5162
|
+
const y0 = Math.min(area.y, painted?.y ?? area.y);
|
|
5163
|
+
const x1 = Math.max(area.x + area.width, painted ? painted.x + painted.width : 0);
|
|
5164
|
+
const y1 = Math.max(area.y + area.height, painted ? painted.y + painted.height : 0);
|
|
5165
|
+
b = { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
5166
|
+
}
|
|
5167
|
+
const rect = { top: b.y, left: b.x, bottom: b.y + b.height, right: b.x + b.width };
|
|
5168
|
+
const values = readRegion(mask.surface, b);
|
|
5169
|
+
const plane = new Uint8Array(values.buffer, values.byteOffset, values.length);
|
|
5170
|
+
return {
|
|
5171
|
+
id: placed,
|
|
5172
|
+
version: 1,
|
|
5173
|
+
rect,
|
|
5174
|
+
depth: 8,
|
|
5175
|
+
maxChannels: 24,
|
|
5176
|
+
slots: Array.from({ length: 26 }, () => null),
|
|
5177
|
+
mask: { rect, plane: { compression: 1, data: encodeRle(plane, { planes: 1, width: b.width, height: b.height, depth: 8, psb: true }) } }
|
|
5178
|
+
};
|
|
5179
|
+
}
|
|
5180
|
+
var warpStyleOf = (name) => {
|
|
5181
|
+
if (!name.startsWith("warp")) return null;
|
|
5182
|
+
const s = name[4]?.toLowerCase() + name.slice(5);
|
|
5183
|
+
return WARP_STYLES.includes(s) ? s : null;
|
|
5184
|
+
};
|
|
5185
|
+
function warpFromDescriptor(w, contents) {
|
|
5186
|
+
const full = { x: 0, y: 0, width: contents.width, height: contents.height };
|
|
5187
|
+
const styleName = enumOf(w, "warpStyle");
|
|
5188
|
+
if (!styleName) return void 0;
|
|
5189
|
+
if (styleName === "warpNone") return { mesh: null, frame: full };
|
|
5190
|
+
const bd = descObject(w, "bounds");
|
|
5191
|
+
if (!bd) return void 0;
|
|
5192
|
+
const left = descNumber(bd, "Left") ?? 0;
|
|
5193
|
+
const top = descNumber(bd, "Top ") ?? 0;
|
|
5194
|
+
const bounds = { x: left, y: top, width: (descNumber(bd, "Rght") ?? 0) - left, height: (descNumber(bd, "Btom") ?? 0) - top };
|
|
5195
|
+
if (!(bounds.width > 0 && bounds.height > 0)) return void 0;
|
|
5196
|
+
let mesh;
|
|
5197
|
+
let frame = full;
|
|
5198
|
+
if (styleName === "warpCustom") {
|
|
5199
|
+
const env = descObject(w, "customEnvelopeWarp");
|
|
5200
|
+
const arr = env ? descGet(env, "meshPoints") : void 0;
|
|
5201
|
+
if (!arr || arr.type !== "ObAr") return void 0;
|
|
5202
|
+
const xs = descGet(arr.body, "Hrzn");
|
|
5203
|
+
const ys = descGet(arr.body, "Vrtc");
|
|
5204
|
+
if (xs?.type !== "UnFl" || ys?.type !== "UnFl" || xs.values.length !== 16 || ys.values.length !== 16) return void 0;
|
|
5205
|
+
mesh = { us: [0, 1], vs: [0, 1], points: xs.values.map((x, i) => ({ x, y: ys.values[i] })) };
|
|
5206
|
+
const px = mesh.points.map((p) => p.x);
|
|
5207
|
+
const py = mesh.points.map((p) => p.y);
|
|
5208
|
+
const f = { x: Math.min(...px), y: Math.min(...py), width: Math.max(...px) - Math.min(...px), height: Math.max(...py) - Math.min(...py) };
|
|
5209
|
+
if (f.width > 1e-9 && f.height > 1e-9 && [f.x, f.y, f.width, f.height].every(Number.isFinite)) frame = f;
|
|
5210
|
+
} else {
|
|
5211
|
+
const style = warpStyleOf(styleName);
|
|
5212
|
+
if (!style) return void 0;
|
|
5213
|
+
mesh = warpPresetMesh(
|
|
5214
|
+
{
|
|
5215
|
+
style,
|
|
5216
|
+
bend: descNumber(w, "warpValue") ?? 0,
|
|
5217
|
+
horizontal: descNumber(w, "warpPerspective") ?? 0,
|
|
5218
|
+
vertical: descNumber(w, "warpPerspectiveOther") ?? 0,
|
|
5219
|
+
orientation: enumOf(w, "warpRotate") === "Vrtc" ? "vertical" : "horizontal"
|
|
5220
|
+
},
|
|
5221
|
+
bounds
|
|
5222
|
+
);
|
|
5223
|
+
if (!mesh) return { mesh: null, frame: full };
|
|
5224
|
+
}
|
|
5225
|
+
const same = Math.abs(bounds.x) < 1e-6 && Math.abs(bounds.y) < 1e-6 && Math.abs(bounds.width - contents.width) < 1e-6 && Math.abs(bounds.height - contents.height) < 1e-6;
|
|
5226
|
+
if (!same) {
|
|
5227
|
+
const m = mesh;
|
|
5228
|
+
mesh = fitWarpMesh((s, t) => evalWarpMesh(m, (s * contents.width - bounds.x) / bounds.width, (t * contents.height - bounds.y) / bounds.height), [0, 1], [0, 1]);
|
|
5229
|
+
}
|
|
5230
|
+
return { mesh, frame };
|
|
5231
|
+
}
|
|
5232
|
+
function warpDescriptor(mesh, contents) {
|
|
5233
|
+
const full = { x: 0, y: 0, width: contents.width, height: contents.height };
|
|
5234
|
+
const single = mesh && mesh.us.length === 2 && mesh.vs.length === 2 && mesh.points.length === 16;
|
|
5235
|
+
const bounds = descriptor("classFloatRect", [
|
|
5236
|
+
["Top ", dbl(0)],
|
|
5237
|
+
["Left", dbl(0)],
|
|
5238
|
+
["Btom", dbl(contents.height)],
|
|
5239
|
+
["Rght", dbl(contents.width)]
|
|
5240
|
+
]);
|
|
5241
|
+
const items = [
|
|
5242
|
+
["warpStyle", enumV("warpStyle", single ? "warpCustom" : "warpNone")],
|
|
5243
|
+
["warpValue", dbl(0)],
|
|
5244
|
+
["warpPerspective", dbl(0)],
|
|
5245
|
+
["warpPerspectiveOther", dbl(0)],
|
|
5246
|
+
["warpRotate", enumV("Ornt", "Hrzn")],
|
|
5247
|
+
["bounds", obj(bounds)],
|
|
5248
|
+
["uOrder", long2(4)],
|
|
5249
|
+
["vOrder", long2(4)]
|
|
5250
|
+
];
|
|
5251
|
+
let frame = full;
|
|
5252
|
+
if (single) {
|
|
5253
|
+
const body = descriptor("rationalPoint", [
|
|
5254
|
+
["Hrzn", { type: "UnFl", unit: "#Pxl", values: mesh.points.map((p) => p.x) }],
|
|
5255
|
+
["Vrtc", { type: "UnFl", unit: "#Pxl", values: mesh.points.map((p) => p.y) }]
|
|
5256
|
+
]);
|
|
5257
|
+
items.push(["customEnvelopeWarp", obj(descriptor("customEnvelopeWarp", [["meshPoints", { type: "ObAr", prefix: 16, body }]]))]);
|
|
5258
|
+
const px = mesh.points.map((p) => p.x);
|
|
5259
|
+
const py = mesh.points.map((p) => p.y);
|
|
5260
|
+
frame = { x: Math.min(...px), y: Math.min(...py), width: Math.max(...px) - Math.min(...px), height: Math.max(...py) - Math.min(...py) };
|
|
5261
|
+
}
|
|
5262
|
+
return { warp: descriptor("warp", items), frame, dropped: Boolean(mesh && !single) };
|
|
5263
|
+
}
|
|
5264
|
+
var isParallelogram = (q2) => {
|
|
5265
|
+
const tol = 1e-6 * (1 + Math.max(...q2.flatMap((p) => [Math.abs(p.x), Math.abs(p.y)])));
|
|
5266
|
+
return Math.abs(q2[0].x + q2[2].x - q2[1].x - q2[3].x) <= tol && Math.abs(q2[0].y + q2[2].y - q2[1].y - q2[3].y) <= tol;
|
|
5267
|
+
};
|
|
5268
|
+
var toQuad = (p) => [
|
|
5269
|
+
{ x: p[0], y: p[1] },
|
|
5270
|
+
{ x: p[2], y: p[3] },
|
|
5271
|
+
{ x: p[4], y: p[5] },
|
|
5272
|
+
{ x: p[6], y: p[7] }
|
|
5273
|
+
];
|
|
5274
|
+
function cornersFromQuad(quad, frame, contents) {
|
|
5275
|
+
if (quad.length !== 8 || !quad.every(Number.isFinite)) return null;
|
|
5276
|
+
const q2 = toQuad(quad);
|
|
5277
|
+
const h = rectToQuad(frame, q2);
|
|
5278
|
+
if (!h) return null;
|
|
5279
|
+
const out = boxQuad({ x: 0, y: 0, width: contents.width, height: contents.height }).map((p) => applyHomography(h, p));
|
|
5280
|
+
return out.every(Boolean) ? out : null;
|
|
5281
|
+
}
|
|
5282
|
+
function quadFromCorners(corners, frame, contents) {
|
|
5283
|
+
const h = rectToQuad({ x: 0, y: 0, width: contents.width, height: contents.height }, corners);
|
|
5284
|
+
const pts = boxQuad(frame).map((p) => (h ? applyHomography(h, p) : p) ?? p);
|
|
5285
|
+
return pts.flatMap((p) => [p.x, p.y]);
|
|
5286
|
+
}
|
|
5287
|
+
var PLACED_KEYS = ["SoLd", "SoLE", "PlLd"];
|
|
5288
|
+
function parsePlaced(key, data) {
|
|
5289
|
+
try {
|
|
5290
|
+
if (key === "SoLd" || key === "SoLE") {
|
|
5291
|
+
const r = new PsdReader(data);
|
|
5292
|
+
if (r.ascii(4) !== "soLD") return null;
|
|
5293
|
+
r.u32();
|
|
5294
|
+
const d = readVersionedDescriptor(r);
|
|
5295
|
+
const quad = numbers(d, "nonAffineTransform") ?? numbers(d, "Trnf") ?? [];
|
|
5296
|
+
const affine = numbers(d, "Trnf");
|
|
5297
|
+
const sz = descObject(d, "Sz ");
|
|
5298
|
+
const size = sz ? { width: descNumber(sz, "Wdth") ?? 0, height: descNumber(sz, "Hght") ?? 0 } : void 0;
|
|
5299
|
+
const use = affine && affine.length === 8 && isParallelogram(toQuad(affine)) && quad.length === 8 && !isParallelogram(toQuad(quad)) ? quad : affine ?? quad;
|
|
5300
|
+
return {
|
|
5301
|
+
key,
|
|
5302
|
+
descriptor: d,
|
|
5303
|
+
idnt: text(d, "Idnt") ?? "",
|
|
5304
|
+
placed: text(d, "placed") ?? "",
|
|
5305
|
+
quad: use,
|
|
5306
|
+
size: size && size.width > 0 && size.height > 0 ? size : void 0,
|
|
5307
|
+
warp: descObject(d, "warp"),
|
|
5308
|
+
filterFX: descObject(d, "filterFX")
|
|
5309
|
+
};
|
|
5310
|
+
}
|
|
5311
|
+
if (key === "PlLd") {
|
|
5312
|
+
const r = new PsdReader(data);
|
|
5313
|
+
if (r.ascii(4) !== "plcL") return null;
|
|
5314
|
+
r.u32();
|
|
5315
|
+
const n = r.u8();
|
|
5316
|
+
const idnt = new TextDecoder().decode(r.bytes(n));
|
|
5317
|
+
r.skip(16);
|
|
5318
|
+
const quad = [];
|
|
5319
|
+
for (let i = 0; i < 8; i++) quad.push(r.f64());
|
|
5320
|
+
let warp;
|
|
5321
|
+
if (r.remaining > 8 && r.peek(r.remaining).some((b) => b !== 0)) {
|
|
5322
|
+
r.u32();
|
|
5323
|
+
try {
|
|
5324
|
+
warp = readVersionedDescriptor(r);
|
|
5325
|
+
} catch {
|
|
5326
|
+
warp = void 0;
|
|
5327
|
+
}
|
|
4876
5328
|
}
|
|
4877
|
-
|
|
4878
|
-
for (let i = 1; i < rb; i++) buf[o + i] = buf[o + i] + buf[o + i - 1] & 255;
|
|
4879
|
-
scratch.set(buf.subarray(o, o + rb));
|
|
4880
|
-
for (let i = 0; i < w; i++) for (let k = 0; k < 4; k++) buf[o + 4 * i + k] = scratch[k * w + i];
|
|
4881
|
-
} else {
|
|
4882
|
-
throw new PsdError(`ZIP prediction is not defined for depth ${l.depth}.`);
|
|
5329
|
+
return { key, idnt, placed: idnt, quad, warp };
|
|
4883
5330
|
}
|
|
5331
|
+
} catch {
|
|
5332
|
+
return null;
|
|
4884
5333
|
}
|
|
5334
|
+
return null;
|
|
4885
5335
|
}
|
|
4886
|
-
function
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
5336
|
+
function placementFromPlaced(p, size, notes) {
|
|
5337
|
+
let mesh = null;
|
|
5338
|
+
let frame = { x: 0, y: 0, width: size.width, height: size.height };
|
|
5339
|
+
if (p.warp) {
|
|
5340
|
+
const w = warpFromDescriptor(p.warp, size);
|
|
5341
|
+
if (!w) notes.push("its warp could not be read and was left out");
|
|
5342
|
+
else {
|
|
5343
|
+
mesh = w.mesh;
|
|
5344
|
+
if (p.quad.length === 8 && isParallelogram(toQuad(p.quad))) frame = w.frame;
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
const corners = cornersFromQuad(p.quad, frame, size);
|
|
5348
|
+
if (!corners) return null;
|
|
5349
|
+
const out = { corners };
|
|
5350
|
+
if (mesh) out.warp = mesh;
|
|
4896
5351
|
return out;
|
|
4897
5352
|
}
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
5353
|
+
var BOX_BLUR = { classId: "boxblur", id: 840, name: "Box Blur..." };
|
|
5354
|
+
function blendOptionsOf(item) {
|
|
5355
|
+
const o = descObject(item, "blendOptions");
|
|
5356
|
+
const opacity = o ? Math.min(1, Math.max(0, (descNumber(o, "Opct") ?? 100) / 100)) : 1;
|
|
5357
|
+
const id = o ? enumOf(o, "Md ") : void 0;
|
|
5358
|
+
return { blend: id && blendFromPsdId(id) || "normal", opacity };
|
|
5359
|
+
}
|
|
5360
|
+
function blendOptions(f) {
|
|
5361
|
+
return obj(
|
|
5362
|
+
descriptor("blendOptions", [
|
|
5363
|
+
["Opct", unit("#Prc", Math.min(1, Math.max(0, f.opacity)) * 100)],
|
|
5364
|
+
["Md ", enumV("BlnM", psdBlendId(f.blend))]
|
|
5365
|
+
])
|
|
5366
|
+
);
|
|
5367
|
+
}
|
|
5368
|
+
function smartFiltersFromPsd(fx, newId, notes) {
|
|
5369
|
+
const filters = [];
|
|
5370
|
+
const list2 = descGet(fx, "filterFXList");
|
|
5371
|
+
for (const v of list2?.type === "VlLs" ? list2.items : []) {
|
|
5372
|
+
if (v.type !== "Objc" && v.type !== "GlbO") continue;
|
|
5373
|
+
const item = v.value;
|
|
5374
|
+
const { blend, opacity } = blendOptionsOf(item);
|
|
5375
|
+
const enabled = descBool(item, "enab") !== false;
|
|
5376
|
+
const fltr = descObject(item, "Fltr");
|
|
5377
|
+
const radius = fltr?.classId === BOX_BLUR.classId ? descNumber(fltr, "Rds ") : void 0;
|
|
5378
|
+
if (radius !== void 0 && radius >= 0 && radius <= 50) {
|
|
5379
|
+
filters.push({ id: newId(), kind: "blur", enabled, radius, blend, opacity });
|
|
5380
|
+
continue;
|
|
4912
5381
|
}
|
|
4913
|
-
|
|
4914
|
-
|
|
5382
|
+
const name = (text(item, "Nm ") ?? "Filter").replace(/&/g, "");
|
|
5383
|
+
const neutral = cloneDescriptor(item);
|
|
5384
|
+
setItem(neutral, "blendOptions", blendOptions({ blend: "normal", opacity: 1 }));
|
|
5385
|
+
setItem(neutral, "enab", boolV(true));
|
|
5386
|
+
filters.push({ id: newId(), kind: "psd", enabled, name, data: versionedDescriptorBytes(neutral), blend, opacity });
|
|
5387
|
+
notes.push(`the ${name.replace(/\.\.\.$/, "")} smart filter is kept but not drawn`);
|
|
4915
5388
|
}
|
|
5389
|
+
return { enabled: descBool(fx, "enab") !== false, filters };
|
|
4916
5390
|
}
|
|
4917
|
-
function
|
|
4918
|
-
const
|
|
4919
|
-
const
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
5391
|
+
function filterFXDescriptor(stack, dropped) {
|
|
5392
|
+
const list2 = [];
|
|
5393
|
+
for (const f of stack.filters) {
|
|
5394
|
+
if (f.kind === "psd") {
|
|
5395
|
+
const d = parseVersionedDescriptor(f.data);
|
|
5396
|
+
setItem(d, "blendOptions", blendOptions(f));
|
|
5397
|
+
setItem(d, "enab", boolV(f.enabled));
|
|
5398
|
+
list2.push(obj(d));
|
|
5399
|
+
} else if (f.kind === "blur") {
|
|
5400
|
+
const rgb = (v) => obj(descriptor("RGBC", [["Rd ", dbl(v)], ["Grn ", dbl(v)], ["Bl ", dbl(v)]]));
|
|
5401
|
+
list2.push(
|
|
5402
|
+
obj(
|
|
5403
|
+
descriptor("filterFX", [
|
|
5404
|
+
["Nm ", textV(BOX_BLUR.name)],
|
|
5405
|
+
["blendOptions", blendOptions(f)],
|
|
5406
|
+
["enab", boolV(f.enabled)],
|
|
5407
|
+
["hasoptions", boolV(true)],
|
|
5408
|
+
["FrgC", rgb(0)],
|
|
5409
|
+
["BckC", rgb(255)],
|
|
5410
|
+
["Fltr", obj(descriptor(BOX_BLUR.classId, [["Rds ", unit("#Pxl", f.radius)]]))],
|
|
5411
|
+
["filterID", long2(BOX_BLUR.id)]
|
|
5412
|
+
])
|
|
5413
|
+
)
|
|
5414
|
+
);
|
|
5415
|
+
} else dropped.push(f.kind);
|
|
5416
|
+
}
|
|
5417
|
+
return descriptor("filterFXStyle", [
|
|
5418
|
+
["enab", boolV(stack.enabled)],
|
|
5419
|
+
["validAtPosition", boolV(true)],
|
|
5420
|
+
["filterMaskEnable", boolV(stack.mask ? stack.mask.enabled !== false : true)],
|
|
5421
|
+
["filterMaskLinked", boolV(stack.mask ? stack.mask.linked !== false : false)],
|
|
5422
|
+
["filterMaskExtendWithWhite", boolV(true)],
|
|
5423
|
+
["filterFXList", { type: "VlLs", items: list2 }]
|
|
5424
|
+
]);
|
|
5425
|
+
}
|
|
5426
|
+
function filterStackKey(stack) {
|
|
5427
|
+
if (!stack) return "";
|
|
5428
|
+
return JSON.stringify([
|
|
5429
|
+
stack.enabled,
|
|
5430
|
+
stack.filters.map((f) => f.kind === "psd" ? ["psd", f.enabled, f.blend, f.opacity, Array.from(f.data)] : { ...f, id: void 0 }),
|
|
5431
|
+
stack.mask ? [stack.mask.enabled !== false, stack.mask.linked !== false] : null
|
|
5432
|
+
]);
|
|
5433
|
+
}
|
|
5434
|
+
function soldBytes(s, notes) {
|
|
5435
|
+
const { warp, frame, dropped } = warpDescriptor(s.placement.warp, s.size);
|
|
5436
|
+
if (dropped) notes.push("a warp of several patches was written without its warp (the layer\u2019s pixels still show it)");
|
|
5437
|
+
const quad = { type: "VlLs", items: quadFromCorners(s.placement.corners, frame, s.size).map(dbl) };
|
|
5438
|
+
let d;
|
|
5439
|
+
if (s.template) {
|
|
5440
|
+
d = cloneDescriptor(s.template);
|
|
5441
|
+
setItem(d, "Idnt", textV(s.idnt));
|
|
5442
|
+
setItem(d, "placed", textV(s.placed));
|
|
5443
|
+
setItem(d, "Trnf", quad);
|
|
5444
|
+
setItem(d, "nonAffineTransform", quad);
|
|
5445
|
+
setItem(d, "warp", obj(warp));
|
|
5446
|
+
setItem(d, "Sz ", obj(descriptor("Pnt ", [["Wdth", dbl(s.size.width)], ["Hght", dbl(s.size.height)]])));
|
|
5447
|
+
if (s.filterFX) setItem(d, "filterFX", obj(s.filterFX));
|
|
5448
|
+
else d.items = d.items.filter(([k]) => k !== "filterFX");
|
|
5449
|
+
} else {
|
|
5450
|
+
const ratio = (n, den) => obj(descriptor("null", [["numerator", long2(n)], ["denominator", long2(den)]]));
|
|
5451
|
+
const items = [
|
|
5452
|
+
["Idnt", textV(s.idnt)],
|
|
5453
|
+
["placed", textV(s.placed)],
|
|
5454
|
+
["PgNm", long2(1)],
|
|
5455
|
+
["totalPages", long2(1)],
|
|
5456
|
+
["Crop", long2(1)],
|
|
5457
|
+
["frameStep", ratio(0, 600)],
|
|
5458
|
+
["duration", ratio(0, 600)],
|
|
5459
|
+
["frameCount", long2(1)],
|
|
5460
|
+
["Annt", long2(16)],
|
|
5461
|
+
["Type", long2(2)],
|
|
5462
|
+
["Trnf", quad],
|
|
5463
|
+
["nonAffineTransform", quad],
|
|
5464
|
+
["warp", obj(warp)],
|
|
5465
|
+
["Sz ", obj(descriptor("Pnt ", [["Wdth", dbl(s.size.width)], ["Hght", dbl(s.size.height)]]))],
|
|
5466
|
+
["Rslt", unit("#Rsl", 72)]
|
|
5467
|
+
];
|
|
5468
|
+
if (s.filterFX) items.push(["filterFX", obj(s.filterFX)]);
|
|
5469
|
+
items.push(["comp", long2(-1)], ["compInfo", obj(descriptor("null", [["compID", long2(-1)], ["originalCompID", long2(-1)]]))]);
|
|
5470
|
+
d = descriptor("null", items);
|
|
4938
5471
|
}
|
|
4939
|
-
const
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
5472
|
+
const w = new PsdWriter();
|
|
5473
|
+
w.ascii("soLD");
|
|
5474
|
+
w.u32(4);
|
|
5475
|
+
writeVersionedDescriptor(w, d);
|
|
5476
|
+
w.zeros((4 - w.length % 4) % 4);
|
|
5477
|
+
return w.result();
|
|
5478
|
+
}
|
|
5479
|
+
function plldBytes(s) {
|
|
5480
|
+
const { warp, frame } = warpDescriptor(s.placement.warp, s.size);
|
|
5481
|
+
const w = new PsdWriter();
|
|
5482
|
+
w.ascii("plcL");
|
|
5483
|
+
w.u32(3);
|
|
5484
|
+
const id = new TextEncoder().encode(s.idnt).subarray(0, 255);
|
|
5485
|
+
w.u8(id.length);
|
|
5486
|
+
w.bytes(id);
|
|
5487
|
+
for (const v of [1, 1, 16, 2]) w.u32(v);
|
|
5488
|
+
for (const v of quadFromCorners(s.placement.corners, frame, s.size)) w.f64(v);
|
|
5489
|
+
w.u32(0);
|
|
5490
|
+
writeVersionedDescriptor(w, warp);
|
|
5491
|
+
w.zeros((4 - w.length % 4) % 4);
|
|
5492
|
+
return w.result();
|
|
4943
5493
|
}
|
|
5494
|
+
var BROWSER_IMAGE_TYPES = {
|
|
5495
|
+
jpg: "image/jpeg",
|
|
5496
|
+
jpeg: "image/jpeg",
|
|
5497
|
+
gif: "image/gif",
|
|
5498
|
+
tif: "image/tiff",
|
|
5499
|
+
tiff: "image/tiff",
|
|
5500
|
+
svg: "image/svg+xml",
|
|
5501
|
+
png: "image/png",
|
|
5502
|
+
webp: "image/webp"
|
|
5503
|
+
};
|
|
4944
5504
|
|
|
4945
5505
|
// src/image-editor/psd/file.ts
|
|
4946
5506
|
var PSD_MAX_DIMENSION = 3e4;
|
|
@@ -4992,7 +5552,7 @@ var SECTION_OTHER = 0;
|
|
|
4992
5552
|
var SECTION_OPEN = 1;
|
|
4993
5553
|
var SECTION_CLOSED = 2;
|
|
4994
5554
|
var SECTION_DIVIDER = 3;
|
|
4995
|
-
function
|
|
5555
|
+
function readRect2(r) {
|
|
4996
5556
|
return { top: r.i32(), left: r.i32(), bottom: r.i32(), right: r.i32() };
|
|
4997
5557
|
}
|
|
4998
5558
|
function rectSize(rect) {
|
|
@@ -5079,8 +5639,8 @@ function realBeforeParameters(d) {
|
|
|
5079
5639
|
function parseMask(d) {
|
|
5080
5640
|
try {
|
|
5081
5641
|
const r = new PsdReader(d);
|
|
5082
|
-
const m = { rect:
|
|
5083
|
-
const readReal = () => r.remaining >= 18 ? { flags: r.u8(), background: r.u8(), rect:
|
|
5642
|
+
const m = { rect: readRect2(r), defaultColor: r.u8(), flags: r.u8() };
|
|
5643
|
+
const readReal = () => r.remaining >= 18 ? { flags: r.u8(), background: r.u8(), rect: readRect2(r) } : void 0;
|
|
5084
5644
|
const params = (m.flags & MASK_FLAG_PARAMETERS) !== 0;
|
|
5085
5645
|
const realFirst = params && realBeforeParameters(d);
|
|
5086
5646
|
if (realFirst) m.real = readReal();
|
|
@@ -5098,7 +5658,7 @@ function parseMask(d) {
|
|
|
5098
5658
|
}
|
|
5099
5659
|
}
|
|
5100
5660
|
function readLayerRecord(r, psb) {
|
|
5101
|
-
const rect =
|
|
5661
|
+
const rect = readRect2(r);
|
|
5102
5662
|
const n = r.u16();
|
|
5103
5663
|
if (n > MAX_LAYER_CHANNELS) throw new PsdError(`A layer has ${n} channels (at most ${MAX_LAYER_CHANNELS}).`);
|
|
5104
5664
|
const channels = [];
|
|
@@ -5249,11 +5809,11 @@ function writeRect(w, r) {
|
|
|
5249
5809
|
}
|
|
5250
5810
|
function writeBlocks(w, blocks, psb, alignment) {
|
|
5251
5811
|
for (const b of blocks) {
|
|
5252
|
-
const
|
|
5253
|
-
w.ascii(
|
|
5812
|
+
const long4 = psb && PSB_LONG_KEYS.has(b.key);
|
|
5813
|
+
w.ascii(long4 ? "8B64" : "8BIM");
|
|
5254
5814
|
w.ascii(b.key);
|
|
5255
5815
|
const pad = (alignment - b.data.length % alignment) % alignment;
|
|
5256
|
-
if (
|
|
5816
|
+
if (long4) w.u64(b.data.length + pad);
|
|
5257
5817
|
else w.u32(b.data.length + pad);
|
|
5258
5818
|
w.bytes(b.data);
|
|
5259
5819
|
w.zeros(pad);
|
|
@@ -5678,6 +6238,7 @@ var FILL_KEYS = ["SoCo", "GdFl", "PtFl"];
|
|
|
5678
6238
|
var TYPE_KEYS = ["TySh", "tySh"];
|
|
5679
6239
|
var SMART_KEYS = ["SoLd", "SoLE", "PlLd"];
|
|
5680
6240
|
var VECTOR_KEYS = ["vmsk", "vsms"];
|
|
6241
|
+
var MAX_CONTENTS_DEPTH = 6;
|
|
5681
6242
|
var percent = (v) => Math.round(v / 255 * 100) / 100;
|
|
5682
6243
|
var Warnings = class {
|
|
5683
6244
|
groups = /* @__PURE__ */ new Map();
|
|
@@ -5896,6 +6457,26 @@ function importNode(ctx, node, depth) {
|
|
|
5896
6457
|
return layer2;
|
|
5897
6458
|
}
|
|
5898
6459
|
const props = baseProps(ctx, rec, name, notes);
|
|
6460
|
+
if (kind === "smart-object" && ctx.skipPixels) {
|
|
6461
|
+
const block = PLACED_KEYS.map((k) => rec.blocks.find((b) => b.key === k)).find(Boolean);
|
|
6462
|
+
const placed = block ? parsePlaced(block.key, block.data) : null;
|
|
6463
|
+
if (placed) {
|
|
6464
|
+
const file = ctx.linked.get(placed.idnt);
|
|
6465
|
+
if (!file || file.kind !== "liFD") notes.push("linked contents come in as the stored pixels");
|
|
6466
|
+
entry.type = "smart";
|
|
6467
|
+
entry.status = notes.length ? "approximated" : "mapped";
|
|
6468
|
+
return { type: "pixel", id: ctx.newId(name), ...props, surface: emptySurface(4), offset: { x: 0, y: 0 } };
|
|
6469
|
+
}
|
|
6470
|
+
}
|
|
6471
|
+
if (kind === "smart-object" && !ctx.skipPixels) {
|
|
6472
|
+
const smart = importSmart(ctx, rec, name, notes, props);
|
|
6473
|
+
if (smart) {
|
|
6474
|
+
entry.type = "smart";
|
|
6475
|
+
entry.id = smart.id;
|
|
6476
|
+
entry.status = notes.length ? "approximated" : "mapped";
|
|
6477
|
+
return smart;
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
5899
6480
|
if (kind !== "pixel") {
|
|
5900
6481
|
const what = kind === "smart-object" ? "Smart objects" : kind === "type" ? "Type layers" : kind === "shape" ? "Shape layers" : "Fill layers";
|
|
5901
6482
|
ctx.warn.add(`${what} came in as pixel layers (their stored pixels)`, name);
|
|
@@ -5913,6 +6494,126 @@ function importNode(ctx, node, depth) {
|
|
|
5913
6494
|
entry.status = notes.length ? "approximated" : "mapped";
|
|
5914
6495
|
return layer;
|
|
5915
6496
|
}
|
|
6497
|
+
function contentsFor(ctx, placed, name, notes) {
|
|
6498
|
+
const memo = ctx.contents.get(placed.idnt);
|
|
6499
|
+
if (memo !== void 0) return memo;
|
|
6500
|
+
const file = ctx.linked.get(placed.idnt);
|
|
6501
|
+
let out = null;
|
|
6502
|
+
if (file && file.kind === "liFD" && file.bytes.length) {
|
|
6503
|
+
const t = fileTypeOf(file.bytes);
|
|
6504
|
+
const base = { id: placed.idnt, name: file.name || `${name}.${t.ext}`, file: { bytes: file.bytes.slice(), type: t.ext } };
|
|
6505
|
+
try {
|
|
6506
|
+
if (t.ext === "psd" || t.ext === "psb") {
|
|
6507
|
+
if (ctx.depth + 1 > MAX_CONTENTS_DEPTH) throw new PsdError("the contents nest too deep");
|
|
6508
|
+
const nested = psdToLayers(readPsd(file.bytes), { newId: ctx.newId, depth: ctx.depth + 1 });
|
|
6509
|
+
for (const w of nested.warnings) ctx.warn.add(`In its contents: ${w.replace(/\.$/, "")}`, name);
|
|
6510
|
+
out = {
|
|
6511
|
+
...base,
|
|
6512
|
+
kind: "document",
|
|
6513
|
+
width: nested.width,
|
|
6514
|
+
height: nested.height,
|
|
6515
|
+
document: { layers: nested.layers, globalLight: nested.globalLight, patterns: nested.patterns.length ? nested.patterns : void 0 }
|
|
6516
|
+
};
|
|
6517
|
+
} else if (t.ext === "png") {
|
|
6518
|
+
const png = decodePng(file.bytes);
|
|
6519
|
+
out = { ...base, kind: "raster", width: png.width, height: png.height, raster: surfaceFromPixels(png, 4, 0, 0) };
|
|
6520
|
+
} else {
|
|
6521
|
+
const size = placed.size ?? { width: 0, height: 0 };
|
|
6522
|
+
if (!(size.width > 0 && size.height > 0)) throw new PsdError("the contents have no stored size");
|
|
6523
|
+
out = { ...base, kind: "raster", width: Math.round(size.width), height: Math.round(size.height) };
|
|
6524
|
+
if (BROWSER_IMAGE_TYPES[t.ext]) {
|
|
6525
|
+
ctx.warn.add(`Smart object contents in ${t.ext.toUpperCase()} format are decoded only in a browser (prepareSmartObjects); until then the layer shows its stored pixels and cannot be re-rendered`, name);
|
|
6526
|
+
} else {
|
|
6527
|
+
ctx.warn.add(`Smart object contents of an unsupported type (${t.ext === "bin" ? "unknown" : t.ext.toUpperCase()}) are kept for export, but the layer shows its stored pixels and cannot be re-rendered`, name);
|
|
6528
|
+
}
|
|
6529
|
+
notes.push(`${t.ext} contents not decoded here`);
|
|
6530
|
+
}
|
|
6531
|
+
} catch (e) {
|
|
6532
|
+
ctx.warn.add(`Smart object contents could not be read (${e instanceof Error ? e.message : String(e)}); the layer came in as its stored pixels`, name);
|
|
6533
|
+
notes.push("contents could not be read");
|
|
6534
|
+
out = null;
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
6537
|
+
ctx.contents.set(placed.idnt, out);
|
|
6538
|
+
return out;
|
|
6539
|
+
}
|
|
6540
|
+
function importSmart(ctx, rec, name, notes, props) {
|
|
6541
|
+
const block = PLACED_KEYS.map((k) => rec.blocks.find((b) => b.key === k)).find(Boolean);
|
|
6542
|
+
if (!block) return null;
|
|
6543
|
+
const placed = parsePlaced(block.key, block.data);
|
|
6544
|
+
if (!placed) return null;
|
|
6545
|
+
const surface = recordPixels(ctx, rec, name);
|
|
6546
|
+
let contents = placed.idnt ? contentsFor(ctx, placed, name, notes) : null;
|
|
6547
|
+
let placement = null;
|
|
6548
|
+
let preview = false;
|
|
6549
|
+
if (contents) {
|
|
6550
|
+
const size = placed.size ?? { width: contents.width, height: contents.height };
|
|
6551
|
+
const pnotes = [];
|
|
6552
|
+
placement = placementFromPlaced(placed, size, pnotes);
|
|
6553
|
+
for (const n of pnotes) {
|
|
6554
|
+
ctx.warn.add(`A smart object's placement was approximated: ${n}`, name);
|
|
6555
|
+
notes.push(n);
|
|
6556
|
+
}
|
|
6557
|
+
if (placement && (size.width !== contents.width || size.height !== contents.height)) {
|
|
6558
|
+
const kx = contents.width / size.width;
|
|
6559
|
+
const ky = contents.height / size.height;
|
|
6560
|
+
if (placement.warp) placement = { ...placement, warp: mapWarpMeshPoints(placement.warp, (p) => ({ x: p.x * kx, y: p.y * ky })) };
|
|
6561
|
+
}
|
|
6562
|
+
}
|
|
6563
|
+
if (!contents || !placement) {
|
|
6564
|
+
const b = surface.bounds;
|
|
6565
|
+
const { width, height } = rectSize(rec.rect);
|
|
6566
|
+
if (!b || !(width > 0 && height > 0)) return null;
|
|
6567
|
+
const file = ctx.linked.get(placed.idnt);
|
|
6568
|
+
contents = {
|
|
6569
|
+
id: placed.idnt || ctx.newId(`${name}-contents`),
|
|
6570
|
+
name: file?.name || `${name}.png`,
|
|
6571
|
+
width,
|
|
6572
|
+
height,
|
|
6573
|
+
kind: "raster",
|
|
6574
|
+
raster: translateSurface(surface, -rec.rect.left, -rec.rect.top)
|
|
6575
|
+
};
|
|
6576
|
+
if (file && file.kind !== "liFD") {
|
|
6577
|
+
contents.linked = { path: file.path ?? file.name };
|
|
6578
|
+
ctx.warn.add(`Linked smart objects came in as their embedded preview; the linked file is not read`, name);
|
|
6579
|
+
notes.push(`linked to "${file.path ?? file.name}"`);
|
|
6580
|
+
} else if (!notes.includes("contents could not be read")) {
|
|
6581
|
+
ctx.warn.add("A smart object without readable contents came in with its stored pixels as contents", name);
|
|
6582
|
+
notes.push("contents missing");
|
|
6583
|
+
}
|
|
6584
|
+
placement = { corners: boxQuad({ x: rec.rect.left, y: rec.rect.top, width, height }) };
|
|
6585
|
+
preview = true;
|
|
6586
|
+
}
|
|
6587
|
+
const layer = { type: "smart", id: ctx.newId(name), ...props, contents, placement, surface, offset: { x: 0, y: 0 } };
|
|
6588
|
+
const fill = u8Block(rec, "iOpa");
|
|
6589
|
+
if (fill !== void 0 && fill < 255) layer.fillOpacity = percent(fill);
|
|
6590
|
+
const effects = recordEffects(ctx, rec, name, notes, false);
|
|
6591
|
+
if (effects) layer.effects = effects;
|
|
6592
|
+
const mask = recordMask(ctx, rec, name);
|
|
6593
|
+
if (mask) layer.mask = mask;
|
|
6594
|
+
if (placed.filterFX) {
|
|
6595
|
+
const fnotes = [];
|
|
6596
|
+
let n = 0;
|
|
6597
|
+
const stack = smartFiltersFromPsd(placed.filterFX, () => `${layer.id}-flt-${++n}`, fnotes);
|
|
6598
|
+
if (preview) {
|
|
6599
|
+
stack.filters = stack.filters.map((f) => f.kind === "psd" ? f : { ...f, enabled: false });
|
|
6600
|
+
}
|
|
6601
|
+
for (const note of fnotes) {
|
|
6602
|
+
ctx.warn.add(`Smart filters were approximated: ${note}`, name);
|
|
6603
|
+
notes.push(note);
|
|
6604
|
+
}
|
|
6605
|
+
const item = ctx.filterItems.get(placed.placed);
|
|
6606
|
+
const fm = item ? filterMaskFromItem(item) : null;
|
|
6607
|
+
if (fm) {
|
|
6608
|
+
if (descBool(placed.filterFX, "filterMaskEnable") === false) fm.enabled = false;
|
|
6609
|
+
if (descBool(placed.filterFX, "filterMaskLinked") === false) fm.linked = false;
|
|
6610
|
+
stack.mask = fm;
|
|
6611
|
+
}
|
|
6612
|
+
if (stack.filters.length || stack.mask || !stack.enabled) layer.smartFilters = stack;
|
|
6613
|
+
}
|
|
6614
|
+
layer.psd = { key: block.key, data: block.data.slice(), placed: placed.placed || void 0 };
|
|
6615
|
+
return layer;
|
|
6616
|
+
}
|
|
5916
6617
|
function mergedLayer(ctx) {
|
|
5917
6618
|
const { header } = ctx.file;
|
|
5918
6619
|
const { width, height } = header;
|
|
@@ -5960,8 +6661,24 @@ function psdToLayers(file, options = {}) {
|
|
|
5960
6661
|
summary: [],
|
|
5961
6662
|
colorCount: colorChannelCount(header.colorMode, header.channels),
|
|
5962
6663
|
palette: header.colorMode === MODE_INDEXED ? file.colorModeData : void 0,
|
|
5963
|
-
patterns: /* @__PURE__ */ new Map()
|
|
6664
|
+
patterns: /* @__PURE__ */ new Map(),
|
|
6665
|
+
linked: /* @__PURE__ */ new Map(),
|
|
6666
|
+
filterItems: /* @__PURE__ */ new Map(),
|
|
6667
|
+
contents: /* @__PURE__ */ new Map(),
|
|
6668
|
+
depth: options.depth ?? 0
|
|
5964
6669
|
};
|
|
6670
|
+
for (const block of file.globalBlocks) {
|
|
6671
|
+
if (LINKED_FILE_KEYS.includes(block.key)) {
|
|
6672
|
+
for (const f of parseLinkedFiles(block.data)) if (!ctx.linked.has(f.uuid)) ctx.linked.set(f.uuid, f);
|
|
6673
|
+
}
|
|
6674
|
+
if (FILTER_EFFECTS_KEYS.includes(block.key)) {
|
|
6675
|
+
try {
|
|
6676
|
+
for (const it of parseFilterEffects(block.data).items) ctx.filterItems.set(it.id, it);
|
|
6677
|
+
} catch (e) {
|
|
6678
|
+
warn.extra.push(`The smart filter masks could not be read (${e instanceof Error ? e.message : String(e)}); they reveal everything.`);
|
|
6679
|
+
}
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
5965
6682
|
for (const block of file.globalBlocks) {
|
|
5966
6683
|
if (!PATTERN_BLOCK_KEYS.includes(block.key)) continue;
|
|
5967
6684
|
try {
|
|
@@ -6064,14 +6781,20 @@ var BLEND_LONG = [
|
|
|
6064
6781
|
];
|
|
6065
6782
|
var BLEND_OF = new Map([...BLEND_CODES, ...BLEND_LONG].map(([m, id]) => [id, m]));
|
|
6066
6783
|
var BLEND_ID = new Map(BLEND_CODES);
|
|
6784
|
+
function blendFromPsdId(id) {
|
|
6785
|
+
return BLEND_OF.get(id);
|
|
6786
|
+
}
|
|
6787
|
+
function psdBlendId(mode) {
|
|
6788
|
+
return BLEND_ID.get(mode) ?? "Nrml";
|
|
6789
|
+
}
|
|
6067
6790
|
var num2 = (d, key, fallback) => descNumber(d, key) ?? fallback;
|
|
6068
6791
|
var pct = (d, key, fallback) => num2(d, key, fallback * 100) / 100;
|
|
6069
6792
|
var bool = (d, key) => descBool(d, key) === true;
|
|
6070
|
-
var
|
|
6793
|
+
var enumOf2 = (d, key) => {
|
|
6071
6794
|
const v = descGet(d, key);
|
|
6072
6795
|
return v?.type === "enum" ? v.value : void 0;
|
|
6073
6796
|
};
|
|
6074
|
-
var
|
|
6797
|
+
var text2 = (d, key) => {
|
|
6075
6798
|
const v = d ? descGet(d, key) : void 0;
|
|
6076
6799
|
return v?.type === "TEXT" ? v.value : "";
|
|
6077
6800
|
};
|
|
@@ -6081,15 +6804,15 @@ var list = (d, key) => {
|
|
|
6081
6804
|
return v.items.flatMap((it) => it.type === "Objc" || it.type === "GlbO" ? [it.value] : []);
|
|
6082
6805
|
};
|
|
6083
6806
|
var clamp3 = (v, lo, hi) => Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : lo;
|
|
6084
|
-
var
|
|
6085
|
-
var
|
|
6086
|
-
var
|
|
6087
|
-
var
|
|
6088
|
-
var
|
|
6089
|
-
var
|
|
6090
|
-
var
|
|
6807
|
+
var unit2 = (u, value) => ({ type: "UntF", unit: u, value });
|
|
6808
|
+
var dbl2 = (value) => ({ type: "doub", value });
|
|
6809
|
+
var long3 = (value) => ({ type: "long", value: Math.round(value) });
|
|
6810
|
+
var boolV2 = (value) => ({ type: "bool", value });
|
|
6811
|
+
var enumV2 = (typeId, value) => ({ type: "enum", typeId, value });
|
|
6812
|
+
var textV2 = (value) => ({ type: "TEXT", value });
|
|
6813
|
+
var obj2 = (d) => ({ type: "Objc", value: d });
|
|
6091
6814
|
function blendOf(d, key, fallback, notes) {
|
|
6092
|
-
const id =
|
|
6815
|
+
const id = enumOf2(d, key);
|
|
6093
6816
|
if (id === void 0) return fallback;
|
|
6094
6817
|
const m = BLEND_OF.get(id);
|
|
6095
6818
|
if (m) return m;
|
|
@@ -6128,15 +6851,15 @@ function colorOf(d, fallback = "#000000") {
|
|
|
6128
6851
|
function colorDesc(hex) {
|
|
6129
6852
|
const [r, g, b] = parseEffectColor(hex) ?? [0, 0, 0];
|
|
6130
6853
|
return descriptor("RGBC", [
|
|
6131
|
-
["Rd ",
|
|
6132
|
-
["Grn ",
|
|
6133
|
-
["Bl ",
|
|
6854
|
+
["Rd ", dbl2(r * 255)],
|
|
6855
|
+
["Grn ", dbl2(g * 255)],
|
|
6856
|
+
["Bl ", dbl2(b * 255)]
|
|
6134
6857
|
]);
|
|
6135
6858
|
}
|
|
6136
6859
|
function contourOf(d, key) {
|
|
6137
6860
|
const c = descObject(d, key);
|
|
6138
6861
|
if (!c) return void 0;
|
|
6139
|
-
const name =
|
|
6862
|
+
const name = text2(c, "Nm ");
|
|
6140
6863
|
const points = list(c, "Crv ").map((p) => {
|
|
6141
6864
|
const out = { input: clamp3(num2(p, "Hrzn", 0) / 255, 0, 1), output: clamp3(num2(p, "Vrtc", 0) / 255, 0, 1) };
|
|
6142
6865
|
if (descBool(p, "Cnty") === false) out.corner = true;
|
|
@@ -6156,16 +6879,16 @@ function contourDesc(c) {
|
|
|
6156
6879
|
type: "VlLs",
|
|
6157
6880
|
items: points.map((p) => {
|
|
6158
6881
|
const items = [
|
|
6159
|
-
["Hrzn",
|
|
6160
|
-
["Vrtc",
|
|
6882
|
+
["Hrzn", dbl2(p.input * 255)],
|
|
6883
|
+
["Vrtc", dbl2(p.output * 255)]
|
|
6161
6884
|
];
|
|
6162
|
-
if (p.corner) items.push(["Cnty",
|
|
6163
|
-
return
|
|
6885
|
+
if (p.corner) items.push(["Cnty", boolV2(false)]);
|
|
6886
|
+
return obj2(descriptor("CrPt", items));
|
|
6164
6887
|
})
|
|
6165
6888
|
};
|
|
6166
|
-
return
|
|
6889
|
+
return obj2(
|
|
6167
6890
|
descriptor("ShpC", [
|
|
6168
|
-
["Nm ",
|
|
6891
|
+
["Nm ", textV2(name)],
|
|
6169
6892
|
["Crv ", pts]
|
|
6170
6893
|
])
|
|
6171
6894
|
);
|
|
@@ -6183,7 +6906,7 @@ function gradientOf(d, notes) {
|
|
|
6183
6906
|
{ position: 0, color: "#000000" },
|
|
6184
6907
|
{ position: 1, color: "#ffffff" }
|
|
6185
6908
|
],
|
|
6186
|
-
style: GRADIENT_STYLES.find(([, id]) => id ===
|
|
6909
|
+
style: GRADIENT_STYLES.find(([, id]) => id === enumOf2(d, "Type"))?.[0] ?? "linear",
|
|
6187
6910
|
angle: clamp3(num2(d, "Angl", 90), -360, 360),
|
|
6188
6911
|
scale: clamp3(pct(d, "Scl ", 1), 0.1, 1.5),
|
|
6189
6912
|
reverse: bool(d, "Rvrs"),
|
|
@@ -6194,12 +6917,12 @@ function gradientOf(d, notes) {
|
|
|
6194
6917
|
if (off) g.offset = { x: clamp3(pct(off, "Hrzn", 0), -1, 1), y: clamp3(pct(off, "Vrtc", 0), -1, 1) };
|
|
6195
6918
|
const grad = descObject(d, "Grad");
|
|
6196
6919
|
if (!grad) return g;
|
|
6197
|
-
if (
|
|
6920
|
+
if (enumOf2(grad, "GrdF") === "ClNs") {
|
|
6198
6921
|
notes.add("noise gradients became black to white");
|
|
6199
6922
|
return g;
|
|
6200
6923
|
}
|
|
6201
6924
|
const raw = list(grad, "Clrs").map((s) => {
|
|
6202
|
-
const type =
|
|
6925
|
+
const type = enumOf2(s, "Type");
|
|
6203
6926
|
if (type === "FrgC" || type === "BckC") notes.add("foreground / background gradient stops became black / white");
|
|
6204
6927
|
const color = type === "BckC" ? "#ffffff" : colorOf(descObject(s, "Clr "));
|
|
6205
6928
|
return { position: clamp3(num2(s, "Lctn", 0) / 4096, 0, 1), color, mid: clamp3(num2(s, "Mdpn", 50) / 100, 0.05, 0.95) };
|
|
@@ -6227,12 +6950,12 @@ function gradientItems(g) {
|
|
|
6227
6950
|
const clrs = {
|
|
6228
6951
|
type: "VlLs",
|
|
6229
6952
|
items: g.stops.map(
|
|
6230
|
-
(s) =>
|
|
6953
|
+
(s) => obj2(
|
|
6231
6954
|
descriptor("Clrt", [
|
|
6232
|
-
["Clr ",
|
|
6233
|
-
["Type",
|
|
6234
|
-
["Lctn",
|
|
6235
|
-
["Mdpn",
|
|
6955
|
+
["Clr ", obj2(colorDesc(s.color))],
|
|
6956
|
+
["Type", enumV2("Clry", "UsrS")],
|
|
6957
|
+
["Lctn", long3(s.position * 4096)],
|
|
6958
|
+
["Mdpn", long3(50)]
|
|
6236
6959
|
])
|
|
6237
6960
|
)
|
|
6238
6961
|
)
|
|
@@ -6241,35 +6964,35 @@ function gradientItems(g) {
|
|
|
6241
6964
|
const trns = {
|
|
6242
6965
|
type: "VlLs",
|
|
6243
6966
|
items: op.map(
|
|
6244
|
-
(s) =>
|
|
6967
|
+
(s) => obj2(
|
|
6245
6968
|
descriptor("TrnS", [
|
|
6246
|
-
["Opct",
|
|
6247
|
-
["Lctn",
|
|
6248
|
-
["Mdpn",
|
|
6969
|
+
["Opct", unit2("#Prc", s.opacity * 100)],
|
|
6970
|
+
["Lctn", long3(s.position * 4096)],
|
|
6971
|
+
["Mdpn", long3(50)]
|
|
6249
6972
|
])
|
|
6250
6973
|
)
|
|
6251
6974
|
)
|
|
6252
6975
|
};
|
|
6253
6976
|
const grad = descriptor("Grdn", [
|
|
6254
|
-
["Nm ",
|
|
6255
|
-
["GrdF",
|
|
6256
|
-
["Intr",
|
|
6977
|
+
["Nm ", textV2("Custom")],
|
|
6978
|
+
["GrdF", enumV2("GrdF", "CstS")],
|
|
6979
|
+
["Intr", dbl2(0)],
|
|
6257
6980
|
["Clrs", clrs],
|
|
6258
6981
|
["Trns", trns]
|
|
6259
6982
|
]);
|
|
6260
6983
|
return [
|
|
6261
|
-
["Grad",
|
|
6262
|
-
["Angl",
|
|
6263
|
-
["Type",
|
|
6264
|
-
["Rvrs",
|
|
6265
|
-
["Algn",
|
|
6266
|
-
["Scl ",
|
|
6984
|
+
["Grad", obj2(grad)],
|
|
6985
|
+
["Angl", unit2("#Ang", g.angle)],
|
|
6986
|
+
["Type", enumV2("GrdT", GRADIENT_STYLES.find(([s]) => s === g.style)[1])],
|
|
6987
|
+
["Rvrs", boolV2(g.reverse)],
|
|
6988
|
+
["Algn", boolV2(g.align)],
|
|
6989
|
+
["Scl ", unit2("#Prc", g.scale * 100)],
|
|
6267
6990
|
[
|
|
6268
6991
|
"Ofst",
|
|
6269
|
-
|
|
6992
|
+
obj2(
|
|
6270
6993
|
descriptor("Pnt ", [
|
|
6271
|
-
["Hrzn",
|
|
6272
|
-
["Vrtc",
|
|
6994
|
+
["Hrzn", unit2("#Prc", g.offset.x * 100)],
|
|
6995
|
+
["Vrtc", unit2("#Prc", g.offset.y * 100)]
|
|
6273
6996
|
])
|
|
6274
6997
|
)
|
|
6275
6998
|
]
|
|
@@ -6277,13 +7000,13 @@ function gradientItems(g) {
|
|
|
6277
7000
|
}
|
|
6278
7001
|
function patternRefOf(d) {
|
|
6279
7002
|
const p = descObject(d, "Ptrn");
|
|
6280
|
-
return { patternId:
|
|
7003
|
+
return { patternId: text2(p, "Idnt") || text2(p, "Nm "), name: text2(p, "Nm ") };
|
|
6281
7004
|
}
|
|
6282
7005
|
function patternRefDesc(patternId, name) {
|
|
6283
|
-
return
|
|
7006
|
+
return obj2(
|
|
6284
7007
|
descriptor("Ptrn", [
|
|
6285
|
-
["Nm ",
|
|
6286
|
-
["Idnt",
|
|
7008
|
+
["Nm ", textV2(name ?? patternId)],
|
|
7009
|
+
["Idnt", textV2(patternId)]
|
|
6287
7010
|
])
|
|
6288
7011
|
);
|
|
6289
7012
|
}
|
|
@@ -6297,14 +7020,14 @@ function placementOf(d) {
|
|
|
6297
7020
|
}
|
|
6298
7021
|
function placementItems(angle, link, phase) {
|
|
6299
7022
|
const out = [];
|
|
6300
|
-
if (angle) out.push(["Angl",
|
|
6301
|
-
out.push(["Algn",
|
|
7023
|
+
if (angle) out.push(["Angl", unit2("#Ang", angle)]);
|
|
7024
|
+
out.push(["Algn", boolV2(link)]);
|
|
6302
7025
|
out.push([
|
|
6303
7026
|
"phase",
|
|
6304
|
-
|
|
7027
|
+
obj2(
|
|
6305
7028
|
descriptor("Pnt ", [
|
|
6306
|
-
["Hrzn",
|
|
6307
|
-
["Vrtc",
|
|
7029
|
+
["Hrzn", dbl2(phase.x)],
|
|
7030
|
+
["Vrtc", dbl2(phase.y)]
|
|
6308
7031
|
])
|
|
6309
7032
|
)
|
|
6310
7033
|
]);
|
|
@@ -6353,7 +7076,7 @@ function readOne(kind, d, notes) {
|
|
|
6353
7076
|
const base = {
|
|
6354
7077
|
...common(d, kind, notes),
|
|
6355
7078
|
fill,
|
|
6356
|
-
technique:
|
|
7079
|
+
technique: enumOf2(d, "GlwT") === "PrBL" ? "precise" : "softer",
|
|
6357
7080
|
size: size("blur", 5),
|
|
6358
7081
|
contour: contourOf(d, "TrnS"),
|
|
6359
7082
|
antiAlias: bool(d, "AntA"),
|
|
@@ -6362,7 +7085,7 @@ function readOne(kind, d, notes) {
|
|
|
6362
7085
|
noise: clamp3(pct(d, "Nose", 0), 0, 1)
|
|
6363
7086
|
};
|
|
6364
7087
|
const spread = clamp3(pct(d, "Ckmt", 0), 0, 1);
|
|
6365
|
-
return kind === "outer-glow" ? { ...base, kind, spread } : { ...base, kind, choke: spread, source:
|
|
7088
|
+
return kind === "outer-glow" ? { ...base, kind, spread } : { ...base, kind, choke: spread, source: enumOf2(d, "glwS") === "SrcC" ? "center" : "edge" };
|
|
6366
7089
|
}
|
|
6367
7090
|
case "bevel-emboss": {
|
|
6368
7091
|
const styles = { OtrB: "outer-bevel", InrB: "inner-bevel", Embs: "emboss", PlEb: "pillow-emboss", strokeEmboss: "stroke-emboss" };
|
|
@@ -6370,10 +7093,10 @@ function readOne(kind, d, notes) {
|
|
|
6370
7093
|
id: newEffectId(),
|
|
6371
7094
|
enabled: descBool(d, "enab") !== false,
|
|
6372
7095
|
kind,
|
|
6373
|
-
style: styles[
|
|
6374
|
-
technique:
|
|
7096
|
+
style: styles[enumOf2(d, "bvlS") ?? ""] ?? "inner-bevel",
|
|
7097
|
+
technique: enumOf2(d, "bvlT") === "PrBL" ? "chisel-hard" : enumOf2(d, "bvlT") === "Slmt" ? "chisel-soft" : "smooth",
|
|
6375
7098
|
depth: clamp3(pct(d, "srgR", 1), 0, 10),
|
|
6376
|
-
direction:
|
|
7099
|
+
direction: enumOf2(d, "bvlD") === "Out " ? "down" : "up",
|
|
6377
7100
|
size: size("blur", 5),
|
|
6378
7101
|
soften: clamp3(num2(d, "Sftn", 0), 0, 16),
|
|
6379
7102
|
angle: clamp3(num2(d, "lagl", 120), -360, 360),
|
|
@@ -6417,14 +7140,14 @@ function readOne(kind, d, notes) {
|
|
|
6417
7140
|
return { ...common(d, kind, notes), kind, patternId: ref.patternId, name: ref.name, scale: clamp3(pct(d, "Scl ", 1), 0.01, 10), angle: place.angle, link: place.link, phase: place.phase };
|
|
6418
7141
|
}
|
|
6419
7142
|
case "stroke": {
|
|
6420
|
-
const paint =
|
|
7143
|
+
const paint = enumOf2(d, "PntT");
|
|
6421
7144
|
let fill;
|
|
6422
7145
|
if (paint === "GrFl") fill = { type: "gradient", gradient: gradientOf(d, notes) };
|
|
6423
7146
|
else if (paint === "Ptrn") {
|
|
6424
7147
|
const ref = patternRefOf(d);
|
|
6425
7148
|
fill = { type: "pattern", patternId: ref.patternId, name: ref.name, scale: clamp3(pct(d, "Scl ", 1), 0.01, 10) };
|
|
6426
7149
|
} else fill = { type: "color", color: colorOf(descObject(d, "Clr ")) };
|
|
6427
|
-
const pos =
|
|
7150
|
+
const pos = enumOf2(d, "Styl");
|
|
6428
7151
|
return { ...common(d, kind, notes), kind, size: size("Sz ", 3), position: pos === "InsF" ? "inside" : pos === "CtrF" ? "center" : "outside", fill };
|
|
6429
7152
|
}
|
|
6430
7153
|
}
|
|
@@ -6451,11 +7174,11 @@ function parseLfx2(data) {
|
|
|
6451
7174
|
}
|
|
6452
7175
|
function commonItems(e) {
|
|
6453
7176
|
return [
|
|
6454
|
-
["enab",
|
|
6455
|
-
["present",
|
|
6456
|
-
["showInDialog",
|
|
6457
|
-
["Md ",
|
|
6458
|
-
["Opct",
|
|
7177
|
+
["enab", boolV2(e.enabled)],
|
|
7178
|
+
["present", boolV2(true)],
|
|
7179
|
+
["showInDialog", boolV2(true)],
|
|
7180
|
+
["Md ", enumV2("BlnM", BLEND_ID.get(e.blend) ?? "Nrml")],
|
|
7181
|
+
["Opct", unit2("#Prc", e.opacity * 100)]
|
|
6459
7182
|
];
|
|
6460
7183
|
}
|
|
6461
7184
|
function writeOne(e) {
|
|
@@ -6465,17 +7188,17 @@ function writeOne(e) {
|
|
|
6465
7188
|
const inner = e.kind === "inner-shadow";
|
|
6466
7189
|
const items = [
|
|
6467
7190
|
...commonItems(e),
|
|
6468
|
-
["Clr ",
|
|
6469
|
-
["uglg",
|
|
6470
|
-
["lagl",
|
|
6471
|
-
["Dstn",
|
|
6472
|
-
["Ckmt",
|
|
6473
|
-
["blur",
|
|
6474
|
-
["Nose",
|
|
6475
|
-
["AntA",
|
|
7191
|
+
["Clr ", obj2(colorDesc(e.color))],
|
|
7192
|
+
["uglg", boolV2(e.useGlobalLight)],
|
|
7193
|
+
["lagl", unit2("#Ang", e.angle)],
|
|
7194
|
+
["Dstn", unit2("#Pxl", e.distance)],
|
|
7195
|
+
["Ckmt", unit2("#Pxl", (inner ? e.choke : e.spread) * 100)],
|
|
7196
|
+
["blur", unit2("#Pxl", e.size)],
|
|
7197
|
+
["Nose", unit2("#Prc", e.noise * 100)],
|
|
7198
|
+
["AntA", boolV2(e.antiAlias)],
|
|
6476
7199
|
["TrnS", contourDesc(e.contour)]
|
|
6477
7200
|
];
|
|
6478
|
-
if (e.kind === "drop-shadow") items.push(["layerConceals",
|
|
7201
|
+
if (e.kind === "drop-shadow") items.push(["layerConceals", boolV2(e.knocksOut)]);
|
|
6479
7202
|
return descriptor(inner ? "IrSh" : "DrSh", items);
|
|
6480
7203
|
}
|
|
6481
7204
|
case "outer-glow":
|
|
@@ -6483,110 +7206,110 @@ function writeOne(e) {
|
|
|
6483
7206
|
const inner = e.kind === "inner-glow";
|
|
6484
7207
|
const items = [...commonItems(e)];
|
|
6485
7208
|
if (e.fill.type === "gradient") items.push(...gradientItems(e.fill.gradient));
|
|
6486
|
-
else items.push(["Clr ",
|
|
7209
|
+
else items.push(["Clr ", obj2(colorDesc(e.fill.color))]);
|
|
6487
7210
|
items.push(
|
|
6488
|
-
["GlwT",
|
|
6489
|
-
["Ckmt",
|
|
6490
|
-
["blur",
|
|
6491
|
-
["Nose",
|
|
6492
|
-
["ShdN",
|
|
6493
|
-
["AntA",
|
|
7211
|
+
["GlwT", enumV2("BETE", e.technique === "precise" ? "PrBL" : "SfBL")],
|
|
7212
|
+
["Ckmt", unit2("#Pxl", (inner ? e.choke : e.spread) * 100)],
|
|
7213
|
+
["blur", unit2("#Pxl", e.size)],
|
|
7214
|
+
["Nose", unit2("#Prc", e.noise * 100)],
|
|
7215
|
+
["ShdN", unit2("#Prc", e.jitter * 100)],
|
|
7216
|
+
["AntA", boolV2(e.antiAlias)],
|
|
6494
7217
|
["TrnS", contourDesc(e.contour)],
|
|
6495
|
-
["Inpr",
|
|
7218
|
+
["Inpr", unit2("#Prc", e.range * 100)]
|
|
6496
7219
|
);
|
|
6497
|
-
if (e.kind === "inner-glow") items.push(["glwS",
|
|
7220
|
+
if (e.kind === "inner-glow") items.push(["glwS", enumV2("IGSr", e.source === "center" ? "SrcC" : "SrcE")]);
|
|
6498
7221
|
return descriptor(inner ? "IrGl" : "OrGl", items);
|
|
6499
7222
|
}
|
|
6500
7223
|
case "bevel-emboss": {
|
|
6501
7224
|
const style = { "outer-bevel": "OtrB", "inner-bevel": "InrB", emboss: "Embs", "pillow-emboss": "PlEb", "stroke-emboss": "strokeEmboss" }[e.style];
|
|
6502
7225
|
const tech = { smooth: "SfBL", "chisel-hard": "PrBL", "chisel-soft": "Slmt" }[e.technique];
|
|
6503
7226
|
const items = [
|
|
6504
|
-
["enab",
|
|
6505
|
-
["present",
|
|
6506
|
-
["showInDialog",
|
|
6507
|
-
["hglM",
|
|
6508
|
-
["hglC",
|
|
6509
|
-
["hglO",
|
|
6510
|
-
["sdwM",
|
|
6511
|
-
["sdwC",
|
|
6512
|
-
["sdwO",
|
|
6513
|
-
["bvlT",
|
|
6514
|
-
["bvlS",
|
|
6515
|
-
["uglg",
|
|
6516
|
-
["lagl",
|
|
6517
|
-
["Lald",
|
|
6518
|
-
["srgR",
|
|
6519
|
-
["blur",
|
|
6520
|
-
["bvlD",
|
|
7227
|
+
["enab", boolV2(e.enabled)],
|
|
7228
|
+
["present", boolV2(true)],
|
|
7229
|
+
["showInDialog", boolV2(true)],
|
|
7230
|
+
["hglM", enumV2("BlnM", BLEND_ID.get(e.highlightBlend) ?? "Scrn")],
|
|
7231
|
+
["hglC", obj2(colorDesc(e.highlightColor))],
|
|
7232
|
+
["hglO", unit2("#Prc", e.highlightOpacity * 100)],
|
|
7233
|
+
["sdwM", enumV2("BlnM", BLEND_ID.get(e.shadowBlend) ?? "Mltp")],
|
|
7234
|
+
["sdwC", obj2(colorDesc(e.shadowColor))],
|
|
7235
|
+
["sdwO", unit2("#Prc", e.shadowOpacity * 100)],
|
|
7236
|
+
["bvlT", enumV2("bvlT", tech)],
|
|
7237
|
+
["bvlS", enumV2("BESl", style)],
|
|
7238
|
+
["uglg", boolV2(e.useGlobalLight)],
|
|
7239
|
+
["lagl", unit2("#Ang", e.angle)],
|
|
7240
|
+
["Lald", unit2("#Ang", e.altitude)],
|
|
7241
|
+
["srgR", unit2("#Prc", e.depth * 100)],
|
|
7242
|
+
["blur", unit2("#Pxl", e.size)],
|
|
7243
|
+
["bvlD", enumV2("BESs", e.direction === "up" ? "In " : "Out ")],
|
|
6521
7244
|
["TrnS", contourDesc(e.glossContour)],
|
|
6522
|
-
["Sftn",
|
|
7245
|
+
["Sftn", unit2("#Pxl", e.soften)]
|
|
6523
7246
|
];
|
|
6524
7247
|
if (e.contourElement) {
|
|
6525
|
-
items.push(["useShape",
|
|
6526
|
-
} else items.push(["useShape",
|
|
7248
|
+
items.push(["useShape", boolV2(true)], ["MpgS", contourDesc(e.contourElement.contour)], ["AntA", boolV2(e.contourElement.antiAlias)], ["Inpr", unit2("#Prc", e.contourElement.range * 100)]);
|
|
7249
|
+
} else items.push(["useShape", boolV2(false)]);
|
|
6527
7250
|
if (e.texture) {
|
|
6528
7251
|
const t = e.texture;
|
|
6529
7252
|
items.push(
|
|
6530
|
-
["useTexture",
|
|
6531
|
-
["InvT",
|
|
6532
|
-
["Scl ",
|
|
6533
|
-
["textureDepth",
|
|
7253
|
+
["useTexture", boolV2(true)],
|
|
7254
|
+
["InvT", boolV2(t.invert)],
|
|
7255
|
+
["Scl ", unit2("#Prc", t.scale * 100)],
|
|
7256
|
+
["textureDepth", unit2("#Prc", t.depth * 100)],
|
|
6534
7257
|
["Ptrn", patternRefDesc(t.patternId, t.name)],
|
|
6535
7258
|
...placementItems(0, t.link ?? true, t.phase ?? { x: 0, y: 0 })
|
|
6536
7259
|
);
|
|
6537
|
-
} else items.push(["useTexture",
|
|
7260
|
+
} else items.push(["useTexture", boolV2(false)]);
|
|
6538
7261
|
return descriptor("ebbl", items);
|
|
6539
7262
|
}
|
|
6540
7263
|
case "satin":
|
|
6541
7264
|
return descriptor("ChFX", [
|
|
6542
7265
|
...commonItems(e),
|
|
6543
|
-
["Clr ",
|
|
6544
|
-
["AntA",
|
|
6545
|
-
["Invr",
|
|
6546
|
-
["lagl",
|
|
6547
|
-
["Dstn",
|
|
6548
|
-
["blur",
|
|
7266
|
+
["Clr ", obj2(colorDesc(e.color))],
|
|
7267
|
+
["AntA", boolV2(e.antiAlias)],
|
|
7268
|
+
["Invr", boolV2(e.invert)],
|
|
7269
|
+
["lagl", unit2("#Ang", e.angle)],
|
|
7270
|
+
["Dstn", unit2("#Pxl", e.distance)],
|
|
7271
|
+
["blur", unit2("#Pxl", e.size)],
|
|
6549
7272
|
["MpgS", contourDesc(e.contour)]
|
|
6550
7273
|
]);
|
|
6551
7274
|
case "color-overlay":
|
|
6552
|
-
return descriptor("SoFi", [...commonItems(e), ["Clr ",
|
|
7275
|
+
return descriptor("SoFi", [...commonItems(e), ["Clr ", obj2(colorDesc(e.color))]]);
|
|
6553
7276
|
case "gradient-overlay":
|
|
6554
|
-
return descriptor("GrFl", [...commonItems(e), ...gradientItems(e.gradient), ["Dthr",
|
|
7277
|
+
return descriptor("GrFl", [...commonItems(e), ...gradientItems(e.gradient), ["Dthr", boolV2(e.dither)]]);
|
|
6555
7278
|
case "pattern-overlay":
|
|
6556
7279
|
return descriptor("patternFill", [
|
|
6557
7280
|
...commonItems(e),
|
|
6558
7281
|
["Ptrn", patternRefDesc(e.patternId, e.name)],
|
|
6559
|
-
["Scl ",
|
|
7282
|
+
["Scl ", unit2("#Prc", e.scale * 100)],
|
|
6560
7283
|
...placementItems(e.angle ?? 0, e.link ?? true, e.phase ?? { x: 0, y: 0 })
|
|
6561
7284
|
]);
|
|
6562
7285
|
case "stroke": {
|
|
6563
7286
|
const items = [
|
|
6564
7287
|
...commonItems(e),
|
|
6565
|
-
["Styl",
|
|
6566
|
-
["Sz ",
|
|
7288
|
+
["Styl", enumV2("FStl", e.position === "inside" ? "InsF" : e.position === "center" ? "CtrF" : "OutF")],
|
|
7289
|
+
["Sz ", unit2("#Pxl", e.size)]
|
|
6567
7290
|
];
|
|
6568
7291
|
const f = e.fill;
|
|
6569
|
-
if (f.type === "color") items.push(["PntT",
|
|
6570
|
-
else if (f.type === "gradient") items.push(["PntT",
|
|
6571
|
-
else items.push(["PntT",
|
|
7292
|
+
if (f.type === "color") items.push(["PntT", enumV2("FrFl", "SClr")], ["Clr ", obj2(colorDesc(f.color))]);
|
|
7293
|
+
else if (f.type === "gradient") items.push(["PntT", enumV2("FrFl", "GrFl")], ...gradientItems(f.gradient));
|
|
7294
|
+
else items.push(["PntT", enumV2("FrFl", "Ptrn")], ["Ptrn", patternRefDesc(f.patternId, f.name)], ["Scl ", unit2("#Prc", f.scale * 100)]);
|
|
6572
7295
|
return descriptor("FrFX", items);
|
|
6573
7296
|
}
|
|
6574
7297
|
}
|
|
6575
7298
|
}
|
|
6576
7299
|
function writeLfx2(effects) {
|
|
6577
7300
|
const items = [
|
|
6578
|
-
["Scl ",
|
|
6579
|
-
["masterFXSwitch",
|
|
7301
|
+
["Scl ", unit2("#Prc", 100)],
|
|
7302
|
+
["masterFXSwitch", boolV2(effects.enabled)]
|
|
6580
7303
|
];
|
|
6581
7304
|
const scale = effectsScale(effects);
|
|
6582
7305
|
let dropped = 0;
|
|
6583
7306
|
for (const [single, multi, kind] of KINDS) {
|
|
6584
7307
|
const of = effects.items.filter((e) => e.kind === kind).map((e) => writeOne(scaleLayerEffect(e, scale)));
|
|
6585
7308
|
if (!of.length) continue;
|
|
6586
|
-
if (of.length === 1) items.push([single,
|
|
6587
|
-
else if (multi) items.push([multi, { type: "VlLs", items: of.map(
|
|
7309
|
+
if (of.length === 1) items.push([single, obj2(of[0])]);
|
|
7310
|
+
else if (multi) items.push([multi, { type: "VlLs", items: of.map(obj2) }]);
|
|
6588
7311
|
else {
|
|
6589
|
-
items.push([single,
|
|
7312
|
+
items.push([single, obj2(of[0])]);
|
|
6590
7313
|
dropped += of.length - 1;
|
|
6591
7314
|
}
|
|
6592
7315
|
}
|
|
@@ -6716,6 +7439,76 @@ function lockBits(layer) {
|
|
|
6716
7439
|
if (!l) return 0;
|
|
6717
7440
|
return (l.all ? 2147483648 : 0) | (l.transparency ? 1 : 0) | (l.pixels ? 2 : 0) | (l.position ? 4 : 0);
|
|
6718
7441
|
}
|
|
7442
|
+
function prepareNestedLayers(layers) {
|
|
7443
|
+
const out = [];
|
|
7444
|
+
for (const l of layers) {
|
|
7445
|
+
if (l.type === "annotation") continue;
|
|
7446
|
+
let next = l;
|
|
7447
|
+
if (l.type === "group") next = { ...l, children: prepareNestedLayers(l.children) };
|
|
7448
|
+
else if (l.type === "pixel" && (l.filters?.length || l.source === "document")) {
|
|
7449
|
+
const p = { ...l };
|
|
7450
|
+
if (l.filters?.some(isActiveFilter)) {
|
|
7451
|
+
const b = l.surface.bounds;
|
|
7452
|
+
if (b) {
|
|
7453
|
+
const r = { x: b.x + l.offset.x - 64, y: b.y + l.offset.y - 64, width: b.width + 128, height: b.height + 128 };
|
|
7454
|
+
const plain = { ...l, visible: true, opacity: 1, blend: "normal" };
|
|
7455
|
+
delete plain.mask;
|
|
7456
|
+
delete plain.clipped;
|
|
7457
|
+
delete plain.effects;
|
|
7458
|
+
delete plain.fillOpacity;
|
|
7459
|
+
const data = new LayerCompositor().composite([plain], r, { width: r.x + r.width, height: r.y + r.height });
|
|
7460
|
+
p.surface = surfaceFromPixels({ width: r.width, height: r.height, data }, 4, r.x - l.offset.x, r.y - l.offset.y);
|
|
7461
|
+
}
|
|
7462
|
+
}
|
|
7463
|
+
delete p.filters;
|
|
7464
|
+
delete p.source;
|
|
7465
|
+
next = p;
|
|
7466
|
+
}
|
|
7467
|
+
if (next.mask?.source) {
|
|
7468
|
+
next = { ...next };
|
|
7469
|
+
delete next.mask;
|
|
7470
|
+
}
|
|
7471
|
+
out.push(next);
|
|
7472
|
+
}
|
|
7473
|
+
return out;
|
|
7474
|
+
}
|
|
7475
|
+
function smartContentsFile(c) {
|
|
7476
|
+
const base = c.name.replace(/\.\w+$/, "") || "Contents";
|
|
7477
|
+
if (c.file) return { name: c.name, bytes: c.file.bytes };
|
|
7478
|
+
if (c.kind === "raster") {
|
|
7479
|
+
const data = readRegion(contentsSurface(c), { x: 0, y: 0, width: c.width, height: c.height });
|
|
7480
|
+
return { name: `${base}.png`, bytes: encodePng(c.width, c.height, data) };
|
|
7481
|
+
}
|
|
7482
|
+
const doc = c.document;
|
|
7483
|
+
const merged = readRegion(contentsSurface(c), { x: 0, y: 0, width: c.width, height: c.height });
|
|
7484
|
+
const res = exportPsd({ width: c.width, height: c.height, layers: prepareNestedLayers(doc.layers), merged, patterns: doc.patterns, globalLight: doc.globalLight });
|
|
7485
|
+
return { name: `${base}.${res.psb ? "psb" : "psd"}`, bytes: res.bytes };
|
|
7486
|
+
}
|
|
7487
|
+
var near = (a, b) => Math.abs(a - b) <= 1e-6 * (1 + Math.abs(a));
|
|
7488
|
+
function placedUnchanged(layer, idnt, placedId) {
|
|
7489
|
+
const raw = layer.psd;
|
|
7490
|
+
if (!raw || raw.key === "PlLd") return false;
|
|
7491
|
+
const p = parsePlaced(raw.key, raw.data);
|
|
7492
|
+
if (!p || p.idnt !== idnt || p.placed !== placedId) return false;
|
|
7493
|
+
const size = p.size ?? { width: layer.contents.width, height: layer.contents.height };
|
|
7494
|
+
if (size.width !== layer.contents.width || size.height !== layer.contents.height) return false;
|
|
7495
|
+
const was = placementFromPlaced(p, size, []);
|
|
7496
|
+
if (!was) return false;
|
|
7497
|
+
const now = placementGeometry(layer);
|
|
7498
|
+
if (!was.corners.every((c, i) => near(c.x, now.corners[i].x) && near(c.y, now.corners[i].y))) return false;
|
|
7499
|
+
const meshKey = (m) => m ? m.points.map((q2) => `${q2.x.toFixed(5)},${q2.y.toFixed(5)}`).join(";") + `|${m.us.length}x${m.vs.length}` : "";
|
|
7500
|
+
if (meshKey(was.warp) !== meshKey(now.warp)) return false;
|
|
7501
|
+
if ((layer.placement.interpolation ?? "bicubic") !== "bicubic") return false;
|
|
7502
|
+
const stored = p.filterFX ? smartFiltersFromPsd(p.filterFX, () => "", []) : void 0;
|
|
7503
|
+
if (stored && p.filterFX) {
|
|
7504
|
+
if (descBool(p.filterFX, "filterMaskEnable") === false || descBool(p.filterFX, "filterMaskLinked") === false) {
|
|
7505
|
+
stored.mask = { surface: layer.surface, defaultValue: 255, enabled: descBool(p.filterFX, "filterMaskEnable") !== false, linked: descBool(p.filterFX, "filterMaskLinked") !== false };
|
|
7506
|
+
}
|
|
7507
|
+
}
|
|
7508
|
+
const current = layer.smartFilters;
|
|
7509
|
+
const norm = (st) => st && (st.filters.length || !st.enabled) ? filterStackKey({ ...st, mask: st.mask && (st.mask.enabled === false || st.mask.linked === false) ? st.mask : void 0 }) : "";
|
|
7510
|
+
return norm(stored) === norm(current);
|
|
7511
|
+
}
|
|
6719
7512
|
function exportPsd(input) {
|
|
6720
7513
|
const { width, height } = input;
|
|
6721
7514
|
if (width < 1 || height < 1) throw new PsdError("The document is empty.");
|
|
@@ -6725,6 +7518,9 @@ function exportPsd(input) {
|
|
|
6725
7518
|
const notes = /* @__PURE__ */ new Map();
|
|
6726
7519
|
const note = (msg, name) => notes.set(msg, [...notes.get(msg) ?? [], name]);
|
|
6727
7520
|
const patternIds2 = /* @__PURE__ */ new Set();
|
|
7521
|
+
const linkedFiles = /* @__PURE__ */ new Map();
|
|
7522
|
+
const filterItems = [];
|
|
7523
|
+
const placedIds = /* @__PURE__ */ new Set();
|
|
6728
7524
|
const effectsBlock = (layer) => {
|
|
6729
7525
|
const fx = layer.effects;
|
|
6730
7526
|
if (!fx) return null;
|
|
@@ -6834,6 +7630,57 @@ function exportPsd(input) {
|
|
|
6834
7630
|
records.push(rec);
|
|
6835
7631
|
return;
|
|
6836
7632
|
}
|
|
7633
|
+
case "smart": {
|
|
7634
|
+
const extra = [];
|
|
7635
|
+
if (layer.fillOpacity !== void 0 && layer.fillOpacity < 1) {
|
|
7636
|
+
extra.push({ key: "iOpa", data: Uint8Array.from([Math.round(Math.max(0, layer.fillOpacity) * 255), 0, 0, 0]) });
|
|
7637
|
+
}
|
|
7638
|
+
const c = layer.contents;
|
|
7639
|
+
const idnt = psdUuid(c.id);
|
|
7640
|
+
if (!linkedFiles.has(idnt)) {
|
|
7641
|
+
if (c.linked) note("Linked smart objects were embedded (their preview is written as the contents)", layer.name);
|
|
7642
|
+
linkedFiles.set(idnt, smartContentsFile(c));
|
|
7643
|
+
}
|
|
7644
|
+
let placedId = layer.psd?.placed ?? psdUuid(`${layer.id}:placed`);
|
|
7645
|
+
if (placedIds.has(placedId)) placedId = psdUuid(`${layer.id}:placed:${placedIds.size}`);
|
|
7646
|
+
placedIds.add(placedId);
|
|
7647
|
+
if (placedUnchanged(layer, idnt, placedId)) {
|
|
7648
|
+
extra.push({ key: layer.psd.key, data: padEven(layer.psd.data) });
|
|
7649
|
+
} else {
|
|
7650
|
+
const dropped = [];
|
|
7651
|
+
const filterFX = layer.smartFilters ? filterFXDescriptor(layer.smartFilters, dropped) : void 0;
|
|
7652
|
+
if (dropped.length) note("Develop smart filters have no Photoshop filter and were left out of the filter list (the layer\u2019s pixels still show them)", layer.name);
|
|
7653
|
+
const raw = layer.psd && layer.psd.key !== "PlLd" ? parsePlaced(layer.psd.key, layer.psd.data) : null;
|
|
7654
|
+
const spec = { idnt, placed: placedId, placement: layer.placement, size: { width: c.width, height: c.height }, filterFX, template: raw?.descriptor };
|
|
7655
|
+
const wnotes = [];
|
|
7656
|
+
extra.push({ key: "PlLd", data: plldBytes(spec) }, { key: "SoLd", data: soldBytes(spec, wnotes) });
|
|
7657
|
+
for (const n2 of wnotes) note(`A smart object: ${n2}`, layer.name);
|
|
7658
|
+
}
|
|
7659
|
+
const fm = layer.smartFilters?.mask;
|
|
7660
|
+
if (fm && !fm.source) filterItems.push(filterMaskItem(placedId, fm, { x: 0, y: 0, width, height }));
|
|
7661
|
+
const rec = { ...common2(layer, extra), rect: EMPTY_RECT, channels: [], blendKey: BLEND_TO_PSD[layer.blend] ?? "norm" };
|
|
7662
|
+
const bounds = contentBounds(layer.surface);
|
|
7663
|
+
if (bounds) {
|
|
7664
|
+
const n2 = bounds.width * bounds.height;
|
|
7665
|
+
const [r2, g2, b2, a2] = rgbaToPlanes(readRegion(layer.surface, bounds), n2);
|
|
7666
|
+
rec.rect = toRect(bounds);
|
|
7667
|
+
rec.channels = [
|
|
7668
|
+
rleChannel(CHANNEL_ALPHA, a2, bounds.width, bounds.height, psb),
|
|
7669
|
+
rleChannel(0, r2, bounds.width, bounds.height, psb),
|
|
7670
|
+
rleChannel(1, g2, bounds.width, bounds.height, psb),
|
|
7671
|
+
rleChannel(2, b2, bounds.width, bounds.height, psb)
|
|
7672
|
+
];
|
|
7673
|
+
} else {
|
|
7674
|
+
rec.channels = [CHANNEL_ALPHA, 0, 1, 2].map(emptyChannel);
|
|
7675
|
+
}
|
|
7676
|
+
if (layer.mask) {
|
|
7677
|
+
const m = maskChannel(layer.mask, 0, 0, psb);
|
|
7678
|
+
rec.channels.push(m.channel);
|
|
7679
|
+
rec.mask = m.mask;
|
|
7680
|
+
}
|
|
7681
|
+
records.push(rec);
|
|
7682
|
+
return;
|
|
7683
|
+
}
|
|
6837
7684
|
case "annotation":
|
|
6838
7685
|
throw new PsdError(`Annotation layer "${layer.name}" must be rasterized before export.`);
|
|
6839
7686
|
}
|
|
@@ -6883,6 +7730,17 @@ function exportPsd(input) {
|
|
|
6883
7730
|
}
|
|
6884
7731
|
if (embed.length) globalBlocks.push({ key: "Patt", data: writePatternBlock(embed) });
|
|
6885
7732
|
}
|
|
7733
|
+
if (linkedFiles.size) {
|
|
7734
|
+
const parts = [...linkedFiles].map(([uuid, f]) => encodeLinkedFile({ uuid, name: f.name, bytes: f.bytes }));
|
|
7735
|
+
const data = new Uint8Array(parts.reduce((n2, p) => n2 + p.length, 0));
|
|
7736
|
+
let at = 0;
|
|
7737
|
+
for (const p of parts) {
|
|
7738
|
+
data.set(p, at);
|
|
7739
|
+
at += p.length;
|
|
7740
|
+
}
|
|
7741
|
+
globalBlocks.push({ key: "lnk2", data });
|
|
7742
|
+
}
|
|
7743
|
+
if (filterItems.length) globalBlocks.push({ key: "FEid", data: writeFilterEffects(filterItems) });
|
|
6886
7744
|
const bytes = writePsd(
|
|
6887
7745
|
{
|
|
6888
7746
|
header: { psb, channels: planes.length, width, height, depth: 8, colorMode: 3 },
|
|
@@ -7147,23 +8005,24 @@ export {
|
|
|
7147
8005
|
deserializeLayer,
|
|
7148
8006
|
deserializeLayers,
|
|
7149
8007
|
PsdError,
|
|
8008
|
+
decodePlanes,
|
|
8009
|
+
encodeRle,
|
|
7150
8010
|
parseVersionedDescriptor,
|
|
7151
8011
|
versionedDescriptorBytes,
|
|
7152
8012
|
ADJUSTMENT_KEYS,
|
|
7153
8013
|
SUPPORTED_ADJUSTMENT_KEYS,
|
|
7154
8014
|
parseAdjustmentBlock,
|
|
7155
8015
|
writeAdjustmentBlock,
|
|
7156
|
-
|
|
7157
|
-
|
|
8016
|
+
parseLfx2,
|
|
8017
|
+
writeLfx2,
|
|
8018
|
+
parseLrfx,
|
|
8019
|
+
BROWSER_IMAGE_TYPES,
|
|
7158
8020
|
readPsd,
|
|
7159
8021
|
layerName,
|
|
7160
8022
|
layerTree,
|
|
7161
8023
|
decodeLayerChannel,
|
|
7162
8024
|
decodeMergedImage,
|
|
7163
8025
|
writePsd,
|
|
7164
|
-
parseLfx2,
|
|
7165
|
-
writeLfx2,
|
|
7166
|
-
parseLrfx,
|
|
7167
8026
|
PATTERN_BLOCK_KEYS,
|
|
7168
8027
|
parsePatternBlock,
|
|
7169
8028
|
psdPatternToEffectPattern,
|
|
@@ -7171,6 +8030,7 @@ export {
|
|
|
7171
8030
|
PSD_BLEND_KEYS,
|
|
7172
8031
|
psdToLayers,
|
|
7173
8032
|
importPsd,
|
|
8033
|
+
smartContentsFile,
|
|
7174
8034
|
exportPsd
|
|
7175
8035
|
};
|
|
7176
|
-
//# sourceMappingURL=chunk-
|
|
8036
|
+
//# sourceMappingURL=chunk-4X5MIVCE.js.map
|