ag-psd 15.0.4 → 15.0.5
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/package.json +1 -1
- package/src/abr.ts +540 -0
- package/src/additionalInfo.ts +2688 -0
- package/src/csh.ts +44 -0
- package/src/descriptor.ts +1804 -0
- package/src/effectsHelpers.ts +305 -0
- package/src/engineData.ts +359 -0
- package/src/helpers.ts +387 -0
- package/src/imageResources.ts +1439 -0
- package/src/index.ts +44 -0
- package/src/initializeCanvas.ts +25 -0
- package/src/jpeg.ts +1166 -0
- package/src/psd.ts +1184 -0
- package/src/psdReader.ts +1091 -0
- package/src/psdWriter.ts +754 -0
- package/src/text.ts +751 -0
- package/src/utf8.ts +160 -0
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { fromByteArray } from 'base64-js';
|
|
2
|
+
import { deflate } from 'pako';
|
|
3
|
+
import { Layer, BlendMode, LayerColor } from './psd';
|
|
4
|
+
|
|
5
|
+
export const MOCK_HANDLERS = false;
|
|
6
|
+
export const RAW_IMAGE_DATA = false;
|
|
7
|
+
|
|
8
|
+
export const fromBlendMode: { [key: string]: string } = {};
|
|
9
|
+
export const toBlendMode: { [key: string]: BlendMode } = {
|
|
10
|
+
'pass': 'pass through',
|
|
11
|
+
'norm': 'normal',
|
|
12
|
+
'diss': 'dissolve',
|
|
13
|
+
'dark': 'darken',
|
|
14
|
+
'mul ': 'multiply',
|
|
15
|
+
'idiv': 'color burn',
|
|
16
|
+
'lbrn': 'linear burn',
|
|
17
|
+
'dkCl': 'darker color',
|
|
18
|
+
'lite': 'lighten',
|
|
19
|
+
'scrn': 'screen',
|
|
20
|
+
'div ': 'color dodge',
|
|
21
|
+
'lddg': 'linear dodge',
|
|
22
|
+
'lgCl': 'lighter color',
|
|
23
|
+
'over': 'overlay',
|
|
24
|
+
'sLit': 'soft light',
|
|
25
|
+
'hLit': 'hard light',
|
|
26
|
+
'vLit': 'vivid light',
|
|
27
|
+
'lLit': 'linear light',
|
|
28
|
+
'pLit': 'pin light',
|
|
29
|
+
'hMix': 'hard mix',
|
|
30
|
+
'diff': 'difference',
|
|
31
|
+
'smud': 'exclusion',
|
|
32
|
+
'fsub': 'subtract',
|
|
33
|
+
'fdiv': 'divide',
|
|
34
|
+
'hue ': 'hue',
|
|
35
|
+
'sat ': 'saturation',
|
|
36
|
+
'colr': 'color',
|
|
37
|
+
'lum ': 'luminosity',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
Object.keys(toBlendMode).forEach(key => fromBlendMode[toBlendMode[key]] = key);
|
|
41
|
+
|
|
42
|
+
export const layerColors: LayerColor[] = [
|
|
43
|
+
'none', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray'
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export const largeAdditionalInfoKeys = [
|
|
47
|
+
// from documentation
|
|
48
|
+
'LMsk', 'Lr16', 'Lr32', 'Layr', 'Mt16', 'Mt32', 'Mtrn', 'Alph', 'FMsk', 'lnk2', 'FEid', 'FXid', 'PxSD',
|
|
49
|
+
// from guessing
|
|
50
|
+
'cinf',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
export interface Dict {
|
|
54
|
+
[key: string]: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function revMap(map: Dict) {
|
|
58
|
+
const result: Dict = {};
|
|
59
|
+
Object.keys(map).forEach(key => result[map[key]] = key);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createEnum<T>(prefix: string, def: string, map: Dict) {
|
|
64
|
+
const rev = revMap(map);
|
|
65
|
+
const decode = (val: string): T => {
|
|
66
|
+
const value = val.split('.')[1];
|
|
67
|
+
if (value && !rev[value]) throw new Error(`Unrecognized value for enum: '${val}'`);
|
|
68
|
+
return (rev[value] as any) || def;
|
|
69
|
+
};
|
|
70
|
+
const encode = (val: T | undefined): string => {
|
|
71
|
+
if (val && !map[val as any]) throw new Error(`Invalid value for enum: '${val}'`);
|
|
72
|
+
return `${prefix}.${map[val as any] || map[def]}`;
|
|
73
|
+
};
|
|
74
|
+
return { decode, encode };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const enum ColorSpace {
|
|
78
|
+
RGB = 0,
|
|
79
|
+
HSB = 1,
|
|
80
|
+
CMYK = 2,
|
|
81
|
+
Lab = 7,
|
|
82
|
+
Grayscale = 8,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const enum LayerMaskFlags {
|
|
86
|
+
PositionRelativeToLayer = 1,
|
|
87
|
+
LayerMaskDisabled = 2,
|
|
88
|
+
InvertLayerMaskWhenBlending = 4, // obsolete
|
|
89
|
+
LayerMaskFromRenderingOtherData = 8,
|
|
90
|
+
MaskHasParametersAppliedToIt = 16,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const enum MaskParams {
|
|
94
|
+
UserMaskDensity = 1,
|
|
95
|
+
UserMaskFeather = 2,
|
|
96
|
+
VectorMaskDensity = 4,
|
|
97
|
+
VectorMaskFeather = 8,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const enum ChannelID {
|
|
101
|
+
Color0 = 0, // red (rgb) / cyan (cmyk)
|
|
102
|
+
Color1 = 1, // green (rgb) / magenta (cmyk)
|
|
103
|
+
Color2 = 2, // blue (rgb) / yellow (cmyk)
|
|
104
|
+
Color3 = 3, // - (rgb) / black (cmyk)
|
|
105
|
+
Transparency = -1,
|
|
106
|
+
UserMask = -2,
|
|
107
|
+
RealUserMask = -3,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const enum Compression {
|
|
111
|
+
RawData = 0,
|
|
112
|
+
RleCompressed = 1,
|
|
113
|
+
ZipWithoutPrediction = 2,
|
|
114
|
+
ZipWithPrediction = 3,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface ChannelData {
|
|
118
|
+
channelId: ChannelID;
|
|
119
|
+
compression: Compression;
|
|
120
|
+
buffer: Uint8Array | undefined;
|
|
121
|
+
length: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface Bounds {
|
|
125
|
+
top: number;
|
|
126
|
+
left: number;
|
|
127
|
+
right: number;
|
|
128
|
+
bottom: number;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface LayerChannelData {
|
|
132
|
+
layer: Layer;
|
|
133
|
+
channels: ChannelData[];
|
|
134
|
+
top: number;
|
|
135
|
+
left: number;
|
|
136
|
+
right: number;
|
|
137
|
+
bottom: number;
|
|
138
|
+
mask?: Bounds;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export type PixelArray = Uint8ClampedArray | Uint8Array;
|
|
142
|
+
|
|
143
|
+
export interface PixelData {
|
|
144
|
+
data: PixelArray;
|
|
145
|
+
width: number;
|
|
146
|
+
height: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function offsetForChannel(channelId: ChannelID, cmyk: boolean) {
|
|
150
|
+
switch (channelId) {
|
|
151
|
+
case ChannelID.Color0: return 0;
|
|
152
|
+
case ChannelID.Color1: return 1;
|
|
153
|
+
case ChannelID.Color2: return 2;
|
|
154
|
+
case ChannelID.Color3: return cmyk ? 3 : channelId + 1;
|
|
155
|
+
case ChannelID.Transparency: return cmyk ? 4 : 3;
|
|
156
|
+
default: return channelId + 1;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function clamp(value: number, min: number, max: number) {
|
|
161
|
+
return value < min ? min : (value > max ? max : value);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function hasAlpha(data: PixelData) {
|
|
165
|
+
const size = data.width * data.height * 4;
|
|
166
|
+
|
|
167
|
+
for (let i = 3; i < size; i += 4) {
|
|
168
|
+
if (data.data[i] !== 255) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function resetImageData({ data }: PixelData) {
|
|
177
|
+
const buffer = new Uint32Array(data.buffer);
|
|
178
|
+
const size = buffer.length | 0;
|
|
179
|
+
|
|
180
|
+
for (let p = 0; p < size; p = (p + 1) | 0) {
|
|
181
|
+
buffer[p] = 0xff000000;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function decodeBitmap(input: PixelArray, output: PixelArray, width: number, height: number) {
|
|
186
|
+
for (let y = 0, p = 0, o = 0; y < height; y++) {
|
|
187
|
+
for (let x = 0; x < width;) {
|
|
188
|
+
let b = input[o++];
|
|
189
|
+
|
|
190
|
+
for (let i = 0; i < 8 && x < width; i++, x++) {
|
|
191
|
+
const v = b & 0x80 ? 0 : 255;
|
|
192
|
+
b = b << 1;
|
|
193
|
+
output[p++] = v;
|
|
194
|
+
output[p++] = v;
|
|
195
|
+
output[p++] = v;
|
|
196
|
+
output[p++] = 255;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function writeDataRaw(data: PixelData, offset: number, width: number, height: number) {
|
|
203
|
+
if (!width || !height)
|
|
204
|
+
return undefined;
|
|
205
|
+
|
|
206
|
+
const array = new Uint8Array(width * height);
|
|
207
|
+
|
|
208
|
+
for (let i = 0; i < array.length; i++) {
|
|
209
|
+
array[i] = data.data[i * 4 + offset];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return array;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function writeDataRLE(buffer: Uint8Array, { data, width, height }: PixelData, offsets: number[], large: boolean) {
|
|
216
|
+
if (!width || !height) return undefined;
|
|
217
|
+
|
|
218
|
+
const stride = (4 * width) | 0;
|
|
219
|
+
|
|
220
|
+
let ol = 0;
|
|
221
|
+
let o = (offsets.length * (large ? 4 : 2) * height) | 0;
|
|
222
|
+
|
|
223
|
+
for (const offset of offsets) {
|
|
224
|
+
for (let y = 0, p = offset | 0; y < height; y++) {
|
|
225
|
+
const strideStart = (y * stride) | 0;
|
|
226
|
+
const strideEnd = (strideStart + stride) | 0;
|
|
227
|
+
const lastIndex = (strideEnd + offset - 4) | 0;
|
|
228
|
+
const lastIndex2 = (lastIndex - 4) | 0;
|
|
229
|
+
const startOffset = o;
|
|
230
|
+
|
|
231
|
+
for (p = (strideStart + offset) | 0; p < strideEnd; p = (p + 4) | 0) {
|
|
232
|
+
if (p < lastIndex2) {
|
|
233
|
+
let value1 = data[p];
|
|
234
|
+
p = (p + 4) | 0;
|
|
235
|
+
let value2 = data[p];
|
|
236
|
+
p = (p + 4) | 0;
|
|
237
|
+
let value3 = data[p];
|
|
238
|
+
|
|
239
|
+
if (value1 === value2 && value1 === value3) {
|
|
240
|
+
let count = 3;
|
|
241
|
+
|
|
242
|
+
while (count < 128 && p < lastIndex && data[(p + 4) | 0] === value1) {
|
|
243
|
+
count = (count + 1) | 0;
|
|
244
|
+
p = (p + 4) | 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
buffer[o++] = 1 - count;
|
|
248
|
+
buffer[o++] = value1;
|
|
249
|
+
} else {
|
|
250
|
+
const countIndex = o;
|
|
251
|
+
let writeLast = true;
|
|
252
|
+
let count = 1;
|
|
253
|
+
buffer[o++] = 0;
|
|
254
|
+
buffer[o++] = value1;
|
|
255
|
+
|
|
256
|
+
while (p < lastIndex && count < 128) {
|
|
257
|
+
p = (p + 4) | 0;
|
|
258
|
+
value1 = value2;
|
|
259
|
+
value2 = value3;
|
|
260
|
+
value3 = data[p];
|
|
261
|
+
|
|
262
|
+
if (value1 === value2 && value1 === value3) {
|
|
263
|
+
p = (p - 12) | 0;
|
|
264
|
+
writeLast = false;
|
|
265
|
+
break;
|
|
266
|
+
} else {
|
|
267
|
+
count++;
|
|
268
|
+
buffer[o++] = value1;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (writeLast) {
|
|
273
|
+
if (count < 127) {
|
|
274
|
+
buffer[o++] = value2;
|
|
275
|
+
buffer[o++] = value3;
|
|
276
|
+
count += 2;
|
|
277
|
+
} else if (count < 128) {
|
|
278
|
+
buffer[o++] = value2;
|
|
279
|
+
count++;
|
|
280
|
+
p = (p - 4) | 0;
|
|
281
|
+
} else {
|
|
282
|
+
p = (p - 8) | 0;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
buffer[countIndex] = count - 1;
|
|
287
|
+
}
|
|
288
|
+
} else if (p === lastIndex) {
|
|
289
|
+
buffer[o++] = 0;
|
|
290
|
+
buffer[o++] = data[p];
|
|
291
|
+
} else { // p === lastIndex2
|
|
292
|
+
buffer[o++] = 1;
|
|
293
|
+
buffer[o++] = data[p];
|
|
294
|
+
p = (p + 4) | 0;
|
|
295
|
+
buffer[o++] = data[p];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const length = o - startOffset;
|
|
300
|
+
|
|
301
|
+
if (large) {
|
|
302
|
+
buffer[ol++] = (length >> 24) & 0xff;
|
|
303
|
+
buffer[ol++] = (length >> 16) & 0xff;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
buffer[ol++] = (length >> 8) & 0xff;
|
|
307
|
+
buffer[ol++] = length & 0xff;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return buffer.slice(0, o);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function writeDataZipWithoutPrediction({ data, width, height }: PixelData, offsets: number[]) {
|
|
315
|
+
const size = width * height;
|
|
316
|
+
const channel = new Uint8Array(size);
|
|
317
|
+
const buffers: Uint8Array[] = [];
|
|
318
|
+
let totalLength = 0;
|
|
319
|
+
|
|
320
|
+
for (const offset of offsets) {
|
|
321
|
+
for (let i = 0, o = offset; i < size; i++, o += 4) {
|
|
322
|
+
channel[i] = data[o];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const buffer = deflate(channel);
|
|
326
|
+
buffers.push(buffer);
|
|
327
|
+
totalLength += buffer.byteLength;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (buffers.length > 0) {
|
|
331
|
+
const buffer = new Uint8Array(totalLength);
|
|
332
|
+
let offset = 0;
|
|
333
|
+
|
|
334
|
+
for (const b of buffers) {
|
|
335
|
+
buffer.set(b, offset);
|
|
336
|
+
offset += b.byteLength;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return buffer;
|
|
340
|
+
} else {
|
|
341
|
+
return buffers[0];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export let createCanvas: (width: number, height: number) => HTMLCanvasElement = () => {
|
|
346
|
+
throw new Error('Canvas not initialized, use initializeCanvas method to set up createCanvas method');
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
export let createCanvasFromData: (data: Uint8Array) => HTMLCanvasElement = () => {
|
|
350
|
+
throw new Error('Canvas not initialized, use initializeCanvas method to set up createCanvasFromData method');
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
let tempCanvas: HTMLCanvasElement | undefined = undefined;
|
|
354
|
+
|
|
355
|
+
export let createImageData: (width: number, height: number) => ImageData = (width, height) => {
|
|
356
|
+
if (!tempCanvas) tempCanvas = createCanvas(1, 1);
|
|
357
|
+
return tempCanvas.getContext('2d')!.createImageData(width, height);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
if (typeof document !== 'undefined') {
|
|
361
|
+
createCanvas = (width, height) => {
|
|
362
|
+
const canvas = document.createElement('canvas');
|
|
363
|
+
canvas.width = width;
|
|
364
|
+
canvas.height = height;
|
|
365
|
+
return canvas;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
createCanvasFromData = (data) => {
|
|
369
|
+
const image = new Image();
|
|
370
|
+
image.src = 'data:image/jpeg;base64,' + fromByteArray(data);
|
|
371
|
+
const canvas = document.createElement('canvas');
|
|
372
|
+
canvas.width = image.width;
|
|
373
|
+
canvas.height = image.height;
|
|
374
|
+
canvas.getContext('2d')!.drawImage(image, 0, 0);
|
|
375
|
+
return canvas;
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export function initializeCanvas(
|
|
380
|
+
createCanvasMethod: (width: number, height: number) => HTMLCanvasElement,
|
|
381
|
+
createCanvasFromDataMethod?: (data: Uint8Array) => HTMLCanvasElement,
|
|
382
|
+
createImageDataMethod?: (width: number, height: number) => ImageData
|
|
383
|
+
) {
|
|
384
|
+
createCanvas = createCanvasMethod;
|
|
385
|
+
createCanvasFromData = createCanvasFromDataMethod || createCanvasFromData;
|
|
386
|
+
createImageData = createImageDataMethod || createImageData;
|
|
387
|
+
}
|