2023-07-27 07:04:13 +00:00
|
|
|
var __assign = (this && this.__assign) || function () {
|
|
|
|
__assign = Object.assign || function(t) {
|
|
|
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
|
|
s = arguments[i];
|
|
|
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
|
|
t[p] = s[p];
|
|
|
|
}
|
|
|
|
return t;
|
|
|
|
};
|
|
|
|
return __assign.apply(this, arguments);
|
|
|
|
};
|
|
|
|
import { hasAlpha, createCanvas, writeDataRLE, offsetForChannel, createImageData, fromBlendMode, clamp, largeAdditionalInfoKeys, RAW_IMAGE_DATA, writeDataZipWithoutPrediction } from './helpers';
|
|
|
|
import { hasMultiEffects, infoHandlers } from './additionalInfo';
|
|
|
|
import { resourceHandlers } from './imageResources';
|
|
|
|
export function createWriter(size) {
|
|
|
|
if (size === void 0) { size = 4096; }
|
|
|
|
var buffer = new ArrayBuffer(size);
|
|
|
|
var view = new DataView(buffer);
|
|
|
|
var offset = 0;
|
|
|
|
return { buffer: buffer, view: view, offset: offset };
|
|
|
|
}
|
|
|
|
export function getWriterBuffer(writer) {
|
|
|
|
return writer.buffer.slice(0, writer.offset);
|
|
|
|
}
|
|
|
|
export function getWriterBufferNoCopy(writer) {
|
|
|
|
return new Uint8Array(writer.buffer, 0, writer.offset);
|
|
|
|
}
|
|
|
|
export function writeUint8(writer, value) {
|
|
|
|
var offset = addSize(writer, 1);
|
|
|
|
writer.view.setUint8(offset, value);
|
|
|
|
}
|
|
|
|
export function writeInt16(writer, value) {
|
|
|
|
var offset = addSize(writer, 2);
|
|
|
|
writer.view.setInt16(offset, value, false);
|
|
|
|
}
|
|
|
|
export function writeUint16(writer, value) {
|
|
|
|
var offset = addSize(writer, 2);
|
|
|
|
writer.view.setUint16(offset, value, false);
|
|
|
|
}
|
|
|
|
export function writeInt32(writer, value) {
|
|
|
|
var offset = addSize(writer, 4);
|
|
|
|
writer.view.setInt32(offset, value, false);
|
|
|
|
}
|
|
|
|
export function writeUint32(writer, value) {
|
|
|
|
var offset = addSize(writer, 4);
|
|
|
|
writer.view.setUint32(offset, value, false);
|
|
|
|
}
|
|
|
|
export function writeFloat32(writer, value) {
|
|
|
|
var offset = addSize(writer, 4);
|
|
|
|
writer.view.setFloat32(offset, value, false);
|
|
|
|
}
|
|
|
|
export function writeFloat64(writer, value) {
|
|
|
|
var offset = addSize(writer, 8);
|
|
|
|
writer.view.setFloat64(offset, value, false);
|
|
|
|
}
|
|
|
|
// 32-bit fixed-point number 16.16
|
|
|
|
export function writeFixedPoint32(writer, value) {
|
|
|
|
writeInt32(writer, value * (1 << 16));
|
|
|
|
}
|
|
|
|
// 32-bit fixed-point number 8.24
|
|
|
|
export function writeFixedPointPath32(writer, value) {
|
|
|
|
writeInt32(writer, value * (1 << 24));
|
|
|
|
}
|
|
|
|
export function writeBytes(writer, buffer) {
|
|
|
|
if (buffer) {
|
|
|
|
ensureSize(writer, writer.offset + buffer.length);
|
|
|
|
var bytes = new Uint8Array(writer.buffer);
|
|
|
|
bytes.set(buffer, writer.offset);
|
|
|
|
writer.offset += buffer.length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export function writeZeros(writer, count) {
|
|
|
|
for (var i = 0; i < count; i++) {
|
|
|
|
writeUint8(writer, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export function writeSignature(writer, signature) {
|
|
|
|
if (signature.length !== 4)
|
|
|
|
throw new Error("Invalid signature: '".concat(signature, "'"));
|
|
|
|
for (var i = 0; i < 4; i++) {
|
|
|
|
writeUint8(writer, signature.charCodeAt(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export function writePascalString(writer, text, padTo) {
|
|
|
|
var length = text.length;
|
|
|
|
writeUint8(writer, length);
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
var code = text.charCodeAt(i);
|
|
|
|
writeUint8(writer, code < 128 ? code : '?'.charCodeAt(0));
|
|
|
|
}
|
|
|
|
while (++length % padTo) {
|
|
|
|
writeUint8(writer, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export function writeUnicodeString(writer, text) {
|
|
|
|
writeUint32(writer, text.length);
|
|
|
|
for (var i = 0; i < text.length; i++) {
|
|
|
|
writeUint16(writer, text.charCodeAt(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export function writeUnicodeStringWithPadding(writer, text) {
|
|
|
|
writeUint32(writer, text.length + 1);
|
|
|
|
for (var i = 0; i < text.length; i++) {
|
|
|
|
writeUint16(writer, text.charCodeAt(i));
|
|
|
|
}
|
|
|
|
writeUint16(writer, 0);
|
|
|
|
}
|
|
|
|
function getLargestLayerSize(layers) {
|
|
|
|
if (layers === void 0) { layers = []; }
|
|
|
|
var max = 0;
|
|
|
|
for (var _i = 0, layers_1 = layers; _i < layers_1.length; _i++) {
|
|
|
|
var layer = layers_1[_i];
|
|
|
|
if (layer.canvas || layer.imageData) {
|
|
|
|
var _a = getLayerDimentions(layer), width = _a.width, height = _a.height;
|
|
|
|
max = Math.max(max, 2 * height + 2 * width * height);
|
|
|
|
}
|
|
|
|
if (layer.children) {
|
|
|
|
max = Math.max(max, getLargestLayerSize(layer.children));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max;
|
|
|
|
}
|
|
|
|
export function writeSection(writer, round, func, writeTotalLength, large) {
|
|
|
|
if (writeTotalLength === void 0) { writeTotalLength = false; }
|
|
|
|
if (large === void 0) { large = false; }
|
|
|
|
if (large)
|
|
|
|
writeUint32(writer, 0);
|
|
|
|
var offset = writer.offset;
|
|
|
|
writeUint32(writer, 0);
|
|
|
|
func();
|
|
|
|
var length = writer.offset - offset - 4;
|
|
|
|
var len = length;
|
|
|
|
while ((len % round) !== 0) {
|
|
|
|
writeUint8(writer, 0);
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
if (writeTotalLength) {
|
|
|
|
length = len;
|
|
|
|
}
|
|
|
|
writer.view.setUint32(offset, length, false);
|
|
|
|
}
|
|
|
|
export function writePsd(writer, psd, options) {
|
|
|
|
if (options === void 0) { options = {}; }
|
|
|
|
if (!(+psd.width > 0 && +psd.height > 0))
|
|
|
|
throw new Error('Invalid document size');
|
|
|
|
if ((psd.width > 30000 || psd.height > 30000) && !options.psb)
|
|
|
|
throw new Error('Document size is too large (max is 30000x30000, use PSB format instead)');
|
|
|
|
var imageResources = psd.imageResources || {};
|
|
|
|
var opt = __assign(__assign({}, options), { layerIds: new Set(), layerToId: new Map() });
|
|
|
|
if (opt.generateThumbnail) {
|
|
|
|
imageResources = __assign(__assign({}, imageResources), { thumbnail: createThumbnail(psd) });
|
|
|
|
}
|
|
|
|
var imageData = psd.imageData;
|
|
|
|
if (!imageData && psd.canvas) {
|
|
|
|
imageData = psd.canvas.getContext('2d').getImageData(0, 0, psd.canvas.width, psd.canvas.height);
|
|
|
|
}
|
|
|
|
if (imageData && (psd.width !== imageData.width || psd.height !== imageData.height))
|
|
|
|
throw new Error('Document canvas must have the same size as document');
|
|
|
|
var globalAlpha = !!imageData && hasAlpha(imageData);
|
|
|
|
var maxBufferSize = Math.max(getLargestLayerSize(psd.children), 4 * 2 * psd.width * psd.height + 2 * psd.height);
|
|
|
|
var tempBuffer = new Uint8Array(maxBufferSize);
|
|
|
|
// header
|
|
|
|
writeSignature(writer, '8BPS');
|
|
|
|
writeUint16(writer, options.psb ? 2 : 1); // version
|
|
|
|
writeZeros(writer, 6);
|
|
|
|
writeUint16(writer, globalAlpha ? 4 : 3); // channels
|
|
|
|
writeUint32(writer, psd.height);
|
|
|
|
writeUint32(writer, psd.width);
|
|
|
|
writeUint16(writer, 8); // bits per channel
|
|
|
|
writeUint16(writer, 3 /* ColorMode.RGB */); // we only support saving RGB right now
|
|
|
|
// color mode data
|
|
|
|
writeSection(writer, 1, function () {
|
|
|
|
// TODO: implement
|
|
|
|
});
|
|
|
|
// image resources
|
|
|
|
writeSection(writer, 1, function () {
|
|
|
|
var _loop_1 = function (handler) {
|
|
|
|
if (handler.has(imageResources)) {
|
|
|
|
writeSignature(writer, '8BIM');
|
|
|
|
writeUint16(writer, handler.key);
|
|
|
|
writePascalString(writer, '', 2);
|
|
|
|
writeSection(writer, 2, function () { return handler.write(writer, imageResources); });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
for (var _i = 0, resourceHandlers_1 = resourceHandlers; _i < resourceHandlers_1.length; _i++) {
|
|
|
|
var handler = resourceHandlers_1[_i];
|
|
|
|
_loop_1(handler);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// layer and mask info
|
|
|
|
writeSection(writer, 2, function () {
|
|
|
|
writeLayerInfo(tempBuffer, writer, psd, globalAlpha, opt);
|
|
|
|
writeGlobalLayerMaskInfo(writer, psd.globalLayerMaskInfo);
|
|
|
|
writeAdditionalLayerInfo(writer, psd, psd, opt);
|
|
|
|
}, undefined, !!opt.psb);
|
|
|
|
// image data
|
|
|
|
var channels = globalAlpha ? [0, 1, 2, 3] : [0, 1, 2];
|
|
|
|
var data = imageData || {
|
|
|
|
data: new Uint8Array(4 * psd.width * psd.height),
|
|
|
|
width: psd.width,
|
|
|
|
height: psd.height,
|
|
|
|
};
|
|
|
|
writeUint16(writer, 1 /* Compression.RleCompressed */); // Photoshop doesn't support zip compression of composite image data
|
|
|
|
if (RAW_IMAGE_DATA && psd.imageDataRaw) {
|
|
|
|
console.log('writing raw image data');
|
|
|
|
writeBytes(writer, psd.imageDataRaw);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
writeBytes(writer, writeDataRLE(tempBuffer, data, channels, !!options.psb));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function writeLayerInfo(tempBuffer, writer, psd, globalAlpha, options) {
|
|
|
|
writeSection(writer, 4, function () {
|
|
|
|
var _a;
|
|
|
|
var layers = [];
|
|
|
|
addChildren(layers, psd.children);
|
|
|
|
if (!layers.length)
|
|
|
|
layers.push({});
|
|
|
|
writeInt16(writer, globalAlpha ? -layers.length : layers.length);
|
|
|
|
var layersData = layers.map(function (l, i) { return getChannels(tempBuffer, l, i === 0, options); });
|
|
|
|
var _loop_2 = function (layerData) {
|
|
|
|
var layer = layerData.layer, top_1 = layerData.top, left = layerData.left, bottom = layerData.bottom, right = layerData.right, channels = layerData.channels;
|
|
|
|
writeInt32(writer, top_1);
|
|
|
|
writeInt32(writer, left);
|
|
|
|
writeInt32(writer, bottom);
|
|
|
|
writeInt32(writer, right);
|
|
|
|
writeUint16(writer, channels.length);
|
|
|
|
for (var _e = 0, channels_1 = channels; _e < channels_1.length; _e++) {
|
|
|
|
var c = channels_1[_e];
|
|
|
|
writeInt16(writer, c.channelId);
|
|
|
|
if (options.psb)
|
|
|
|
writeUint32(writer, 0);
|
|
|
|
writeUint32(writer, c.length);
|
|
|
|
}
|
|
|
|
writeSignature(writer, '8BIM');
|
|
|
|
writeSignature(writer, fromBlendMode[layer.blendMode] || 'norm');
|
|
|
|
writeUint8(writer, Math.round(clamp((_a = layer.opacity) !== null && _a !== void 0 ? _a : 1, 0, 1) * 255));
|
|
|
|
writeUint8(writer, layer.clipping ? 1 : 0);
|
|
|
|
var flags = 0x08; // 1 for Photoshop 5.0 and later, tells if bit 4 has useful information
|
|
|
|
if (layer.transparencyProtected)
|
|
|
|
flags |= 0x01;
|
|
|
|
if (layer.hidden)
|
|
|
|
flags |= 0x02;
|
|
|
|
if (layer.vectorMask || (layer.sectionDivider && layer.sectionDivider.type !== 0 /* SectionDividerType.Other */)) {
|
|
|
|
flags |= 0x10; // pixel data irrelevant to appearance of document
|
|
|
|
}
|
|
|
|
if (layer.effects && hasMultiEffects(layer.effects)) { // TODO: this is not correct
|
|
|
|
flags |= 0x20; // just guessing this one, might be completely incorrect
|
|
|
|
}
|
|
|
|
// if ('_2' in layer) flags |= 0x20; // TEMP!!!
|
|
|
|
writeUint8(writer, flags);
|
|
|
|
writeUint8(writer, 0); // filler
|
|
|
|
writeSection(writer, 1, function () {
|
|
|
|
writeLayerMaskData(writer, layer, layerData);
|
|
|
|
writeLayerBlendingRanges(writer, psd);
|
|
|
|
writePascalString(writer, layer.name || '', 4);
|
|
|
|
writeAdditionalLayerInfo(writer, layer, psd, options);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
// layer records
|
|
|
|
for (var _i = 0, layersData_1 = layersData; _i < layersData_1.length; _i++) {
|
|
|
|
var layerData = layersData_1[_i];
|
|
|
|
_loop_2(layerData);
|
|
|
|
}
|
|
|
|
// layer channel image data
|
|
|
|
for (var _b = 0, layersData_2 = layersData; _b < layersData_2.length; _b++) {
|
|
|
|
var layerData = layersData_2[_b];
|
|
|
|
for (var _c = 0, _d = layerData.channels; _c < _d.length; _c++) {
|
|
|
|
var channel = _d[_c];
|
|
|
|
writeUint16(writer, channel.compression);
|
|
|
|
if (channel.buffer) {
|
|
|
|
writeBytes(writer, channel.buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, true, options.psb);
|
|
|
|
}
|
|
|
|
function writeLayerMaskData(writer, _a, layerData) {
|
|
|
|
var mask = _a.mask;
|
|
|
|
writeSection(writer, 1, function () {
|
|
|
|
if (!mask)
|
|
|
|
return;
|
|
|
|
var m = layerData.mask || {};
|
|
|
|
writeInt32(writer, m.top);
|
|
|
|
writeInt32(writer, m.left);
|
|
|
|
writeInt32(writer, m.bottom);
|
|
|
|
writeInt32(writer, m.right);
|
|
|
|
writeUint8(writer, mask.defaultColor);
|
|
|
|
var params = 0;
|
|
|
|
if (mask.userMaskDensity !== undefined)
|
|
|
|
params |= 1 /* MaskParams.UserMaskDensity */;
|
|
|
|
if (mask.userMaskFeather !== undefined)
|
|
|
|
params |= 2 /* MaskParams.UserMaskFeather */;
|
|
|
|
if (mask.vectorMaskDensity !== undefined)
|
|
|
|
params |= 4 /* MaskParams.VectorMaskDensity */;
|
|
|
|
if (mask.vectorMaskFeather !== undefined)
|
|
|
|
params |= 8 /* MaskParams.VectorMaskFeather */;
|
|
|
|
var flags = 0;
|
|
|
|
if (mask.disabled)
|
|
|
|
flags |= 2 /* LayerMaskFlags.LayerMaskDisabled */;
|
|
|
|
if (mask.positionRelativeToLayer)
|
|
|
|
flags |= 1 /* LayerMaskFlags.PositionRelativeToLayer */;
|
|
|
|
if (mask.fromVectorData)
|
|
|
|
flags |= 8 /* LayerMaskFlags.LayerMaskFromRenderingOtherData */;
|
|
|
|
if (params)
|
|
|
|
flags |= 16 /* LayerMaskFlags.MaskHasParametersAppliedToIt */;
|
|
|
|
writeUint8(writer, flags);
|
|
|
|
if (params) {
|
|
|
|
writeUint8(writer, params);
|
|
|
|
if (mask.userMaskDensity !== undefined)
|
|
|
|
writeUint8(writer, Math.round(mask.userMaskDensity * 0xff));
|
|
|
|
if (mask.userMaskFeather !== undefined)
|
|
|
|
writeFloat64(writer, mask.userMaskFeather);
|
|
|
|
if (mask.vectorMaskDensity !== undefined)
|
|
|
|
writeUint8(writer, Math.round(mask.vectorMaskDensity * 0xff));
|
|
|
|
if (mask.vectorMaskFeather !== undefined)
|
|
|
|
writeFloat64(writer, mask.vectorMaskFeather);
|
|
|
|
}
|
|
|
|
// TODO: handle rest of the fields
|
|
|
|
writeZeros(writer, 2);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function writeLayerBlendingRanges(writer, psd) {
|
|
|
|
writeSection(writer, 1, function () {
|
|
|
|
writeUint32(writer, 65535);
|
|
|
|
writeUint32(writer, 65535);
|
|
|
|
var channels = psd.channels || 0; // TODO: use always 4 instead ?
|
|
|
|
// channels = 4; // TESTING
|
|
|
|
for (var i = 0; i < channels; i++) {
|
|
|
|
writeUint32(writer, 65535);
|
|
|
|
writeUint32(writer, 65535);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function writeGlobalLayerMaskInfo(writer, info) {
|
|
|
|
writeSection(writer, 1, function () {
|
|
|
|
if (info) {
|
|
|
|
writeUint16(writer, info.overlayColorSpace);
|
|
|
|
writeUint16(writer, info.colorSpace1);
|
|
|
|
writeUint16(writer, info.colorSpace2);
|
|
|
|
writeUint16(writer, info.colorSpace3);
|
|
|
|
writeUint16(writer, info.colorSpace4);
|
|
|
|
writeUint16(writer, info.opacity * 0xff);
|
|
|
|
writeUint8(writer, info.kind);
|
|
|
|
writeZeros(writer, 3);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function writeAdditionalLayerInfo(writer, target, psd, options) {
|
|
|
|
var _loop_3 = function (handler) {
|
|
|
|
var key = handler.key;
|
|
|
|
if (key === 'Txt2' && options.invalidateTextLayers)
|
|
|
|
return "continue";
|
|
|
|
if (key === 'vmsk' && options.psb)
|
|
|
|
key = 'vsms';
|
|
|
|
if (handler.has(target)) {
|
|
|
|
var large = options.psb && largeAdditionalInfoKeys.indexOf(key) !== -1;
|
|
|
|
writeSignature(writer, large ? '8B64' : '8BIM');
|
|
|
|
writeSignature(writer, key);
|
|
|
|
var fourBytes = key === 'Txt2' || key === 'luni' || key === 'vmsk' || key === 'artb' || key === 'artd' ||
|
|
|
|
key === 'vogk' || key === 'SoLd' || key === 'lnk2' || key === 'vscg' || key === 'vsms' || key === 'GdFl' ||
|
|
|
|
key === 'lmfx' || key === 'lrFX' || key === 'cinf' || key === 'PlLd' || key === 'Anno';
|
|
|
|
writeSection(writer, fourBytes ? 4 : 2, function () {
|
|
|
|
handler.write(writer, target, psd, options);
|
|
|
|
}, key !== 'Txt2' && key !== 'cinf' && key !== 'extn', large);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
for (var _i = 0, infoHandlers_1 = infoHandlers; _i < infoHandlers_1.length; _i++) {
|
|
|
|
var handler = infoHandlers_1[_i];
|
|
|
|
_loop_3(handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function addChildren(layers, children) {
|
|
|
|
if (!children)
|
|
|
|
return;
|
|
|
|
for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
|
|
|
|
var c = children_1[_i];
|
|
|
|
if (c.children && c.canvas)
|
|
|
|
throw new Error("Invalid layer, cannot have both 'canvas' and 'children' properties");
|
|
|
|
if (c.children && c.imageData)
|
|
|
|
throw new Error("Invalid layer, cannot have both 'imageData' and 'children' properties");
|
|
|
|
if (c.children) {
|
|
|
|
layers.push({
|
|
|
|
name: '</Layer group>',
|
|
|
|
sectionDivider: {
|
|
|
|
type: 3 /* SectionDividerType.BoundingSectionDivider */,
|
|
|
|
},
|
|
|
|
// TESTING
|
|
|
|
// nameSource: 'lset',
|
|
|
|
// id: [4, 0, 0, 8, 11, 0, 0, 0, 0, 14][layers.length] || 0,
|
|
|
|
// layerColor: 'none',
|
|
|
|
// timestamp: [1611346817.349021, 0, 0, 1611346817.349175, 1611346817.3491833, 0, 0, 0, 0, 1611346817.349832][layers.length] || 0,
|
|
|
|
// protected: {},
|
|
|
|
// referencePoint: { x: 0, y: 0 },
|
|
|
|
});
|
|
|
|
addChildren(layers, c.children);
|
|
|
|
layers.push(__assign({ sectionDivider: {
|
|
|
|
type: c.opened === false ? 2 /* SectionDividerType.ClosedFolder */ : 1 /* SectionDividerType.OpenFolder */,
|
|
|
|
key: fromBlendMode[c.blendMode] || 'pass',
|
|
|
|
subType: 0,
|
|
|
|
} }, c));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
layers.push(__assign({}, c));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function resizeBuffer(writer, size) {
|
|
|
|
var newLength = writer.buffer.byteLength;
|
|
|
|
do {
|
|
|
|
newLength *= 2;
|
|
|
|
} while (size > newLength);
|
|
|
|
var newBuffer = new ArrayBuffer(newLength);
|
|
|
|
var newBytes = new Uint8Array(newBuffer);
|
|
|
|
var oldBytes = new Uint8Array(writer.buffer);
|
|
|
|
newBytes.set(oldBytes);
|
|
|
|
writer.buffer = newBuffer;
|
|
|
|
writer.view = new DataView(writer.buffer);
|
|
|
|
}
|
|
|
|
function ensureSize(writer, size) {
|
|
|
|
if (size > writer.buffer.byteLength) {
|
|
|
|
resizeBuffer(writer, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function addSize(writer, size) {
|
|
|
|
var offset = writer.offset;
|
|
|
|
ensureSize(writer, writer.offset += size);
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
function createThumbnail(psd) {
|
|
|
|
var canvas = createCanvas(10, 10);
|
|
|
|
var scale = 1;
|
|
|
|
if (psd.width > psd.height) {
|
|
|
|
canvas.width = 160;
|
|
|
|
canvas.height = Math.floor(psd.height * (canvas.width / psd.width));
|
|
|
|
scale = canvas.width / psd.width;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
canvas.height = 160;
|
|
|
|
canvas.width = Math.floor(psd.width * (canvas.height / psd.height));
|
|
|
|
scale = canvas.height / psd.height;
|
|
|
|
}
|
|
|
|
var context = canvas.getContext('2d');
|
|
|
|
context.scale(scale, scale);
|
|
|
|
if (psd.imageData) {
|
|
|
|
var temp = createCanvas(psd.imageData.width, psd.imageData.height);
|
|
|
|
temp.getContext('2d').putImageData(psd.imageData, 0, 0);
|
|
|
|
context.drawImage(temp, 0, 0);
|
|
|
|
}
|
|
|
|
else if (psd.canvas) {
|
|
|
|
context.drawImage(psd.canvas, 0, 0);
|
|
|
|
}
|
|
|
|
return canvas;
|
|
|
|
}
|
|
|
|
function getChannels(tempBuffer, layer, background, options) {
|
|
|
|
var layerData = getLayerChannels(tempBuffer, layer, background, options);
|
|
|
|
var mask = layer.mask;
|
|
|
|
if (mask) {
|
|
|
|
var top_2 = mask.top | 0;
|
|
|
|
var left = mask.left | 0;
|
|
|
|
var right = mask.right | 0;
|
|
|
|
var bottom = mask.bottom | 0;
|
|
|
|
var _a = getLayerDimentions(mask), width = _a.width, height = _a.height;
|
|
|
|
var imageData = mask.imageData;
|
|
|
|
if (!imageData && mask.canvas && width && height) {
|
|
|
|
imageData = mask.canvas.getContext('2d').getImageData(0, 0, width, height);
|
|
|
|
}
|
|
|
|
if (width && height && imageData) {
|
|
|
|
right = left + width;
|
|
|
|
bottom = top_2 + height;
|
|
|
|
if (imageData.width !== width || imageData.height !== height) {
|
|
|
|
throw new Error('Invalid imageData dimentions');
|
|
|
|
}
|
|
|
|
var buffer = void 0;
|
|
|
|
var compression = void 0;
|
|
|
|
if (RAW_IMAGE_DATA && layer.maskDataRaw) {
|
|
|
|
// console.log('written raw layer image data');
|
|
|
|
buffer = layer.maskDataRaw;
|
|
|
|
compression = 1 /* Compression.RleCompressed */;
|
|
|
|
}
|
|
|
|
else if (options.compress) {
|
|
|
|
buffer = writeDataZipWithoutPrediction(imageData, [0]);
|
|
|
|
compression = 2 /* Compression.ZipWithoutPrediction */;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
buffer = writeDataRLE(tempBuffer, imageData, [0], !!options.psb);
|
|
|
|
compression = 1 /* Compression.RleCompressed */;
|
|
|
|
}
|
|
|
|
layerData.mask = { top: top_2, left: left, right: right, bottom: bottom };
|
|
|
|
layerData.channels.push({ channelId: -2 /* ChannelID.UserMask */, compression: compression, buffer: buffer, length: 2 + buffer.length });
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
layerData.mask = { top: 0, left: 0, right: 0, bottom: 0 };
|
|
|
|
layerData.channels.push({ channelId: -2 /* ChannelID.UserMask */, compression: 0 /* Compression.RawData */, buffer: new Uint8Array(0), length: 0 });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return layerData;
|
|
|
|
}
|
|
|
|
function getLayerDimentions(_a) {
|
|
|
|
var canvas = _a.canvas, imageData = _a.imageData;
|
|
|
|
return imageData || canvas || { width: 0, height: 0 };
|
|
|
|
}
|
|
|
|
function cropImageData(data, left, top, width, height) {
|
|
|
|
var croppedData = createImageData(width, height);
|
|
|
|
var srcData = data.data;
|
|
|
|
var dstData = croppedData.data;
|
|
|
|
for (var y = 0; y < height; y++) {
|
|
|
|
for (var x = 0; x < width; x++) {
|
|
|
|
var src = ((x + left) + (y + top) * width) * 4;
|
|
|
|
var dst = (x + y * width) * 4;
|
|
|
|
dstData[dst] = srcData[src];
|
|
|
|
dstData[dst + 1] = srcData[src + 1];
|
|
|
|
dstData[dst + 2] = srcData[src + 2];
|
|
|
|
dstData[dst + 3] = srcData[src + 3];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return croppedData;
|
|
|
|
}
|
|
|
|
function getLayerChannels(tempBuffer, layer, background, options) {
|
|
|
|
var _a;
|
|
|
|
var top = layer.top | 0;
|
|
|
|
var left = layer.left | 0;
|
|
|
|
var right = layer.right | 0;
|
|
|
|
var bottom = layer.bottom | 0;
|
|
|
|
var channels = [
|
|
|
|
{ channelId: -1 /* ChannelID.Transparency */, compression: 0 /* Compression.RawData */, buffer: undefined, length: 2 },
|
|
|
|
{ channelId: 0 /* ChannelID.Color0 */, compression: 0 /* Compression.RawData */, buffer: undefined, length: 2 },
|
|
|
|
{ channelId: 1 /* ChannelID.Color1 */, compression: 0 /* Compression.RawData */, buffer: undefined, length: 2 },
|
|
|
|
{ channelId: 2 /* ChannelID.Color2 */, compression: 0 /* Compression.RawData */, buffer: undefined, length: 2 },
|
|
|
|
];
|
|
|
|
var _b = getLayerDimentions(layer), width = _b.width, height = _b.height;
|
|
|
|
if (!(layer.canvas || layer.imageData) || !width || !height) {
|
|
|
|
right = left;
|
|
|
|
bottom = top;
|
|
|
|
return { layer: layer, top: top, left: left, right: right, bottom: bottom, channels: channels };
|
|
|
|
}
|
|
|
|
right = left + width;
|
|
|
|
bottom = top + height;
|
|
|
|
var data = layer.imageData || layer.canvas.getContext('2d').getImageData(0, 0, width, height);
|
|
|
|
if (options.trimImageData) {
|
|
|
|
var trimmed = trimData(data);
|
|
|
|
if (trimmed.left !== 0 || trimmed.top !== 0 || trimmed.right !== data.width || trimmed.bottom !== data.height) {
|
|
|
|
left += trimmed.left;
|
|
|
|
top += trimmed.top;
|
|
|
|
right -= (data.width - trimmed.right);
|
|
|
|
bottom -= (data.height - trimmed.bottom);
|
|
|
|
width = right - left;
|
|
|
|
height = bottom - top;
|
|
|
|
if (!width || !height) {
|
|
|
|
return { layer: layer, top: top, left: left, right: right, bottom: bottom, channels: channels };
|
|
|
|
}
|
|
|
|
if (layer.imageData) {
|
|
|
|
data = cropImageData(data, trimmed.left, trimmed.top, width, height);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
data = layer.canvas.getContext('2d').getImageData(trimmed.left, trimmed.top, width, height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var channelIds = [
|
|
|
|
0 /* ChannelID.Color0 */,
|
|
|
|
1 /* ChannelID.Color1 */,
|
|
|
|
2 /* ChannelID.Color2 */,
|
|
|
|
];
|
|
|
|
if (!background || options.noBackground || layer.mask || hasAlpha(data) || (RAW_IMAGE_DATA && ((_a = layer.imageDataRaw) === null || _a === void 0 ? void 0 : _a['-1']))) {
|
|
|
|
channelIds.unshift(-1 /* ChannelID.Transparency */);
|
|
|
|
}
|
|
|
|
channels = channelIds.map(function (channelId) {
|
|
|
|
var offset = offsetForChannel(channelId, false); // TODO: psd.colorMode === ColorMode.CMYK);
|
|
|
|
var buffer;
|
|
|
|
var compression;
|
|
|
|
if (RAW_IMAGE_DATA && layer.imageDataRaw) {
|
|
|
|
// console.log('written raw layer image data');
|
|
|
|
buffer = layer.imageDataRaw[channelId];
|
|
|
|
compression = 1 /* Compression.RleCompressed */;
|
|
|
|
}
|
|
|
|
else if (options.compress) {
|
|
|
|
buffer = writeDataZipWithoutPrediction(data, [offset]);
|
|
|
|
compression = 2 /* Compression.ZipWithoutPrediction */;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
buffer = writeDataRLE(tempBuffer, data, [offset], !!options.psb);
|
|
|
|
compression = 1 /* Compression.RleCompressed */;
|
|
|
|
}
|
|
|
|
return { channelId: channelId, compression: compression, buffer: buffer, length: 2 + buffer.length };
|
|
|
|
});
|
|
|
|
return { layer: layer, top: top, left: left, right: right, bottom: bottom, channels: channels };
|
|
|
|
}
|
|
|
|
function isRowEmpty(_a, y, left, right) {
|
|
|
|
var data = _a.data, width = _a.width;
|
|
|
|
var start = ((y * width + left) * 4 + 3) | 0;
|
|
|
|
var end = (start + (right - left) * 4) | 0;
|
|
|
|
for (var i = start; i < end; i = (i + 4) | 0) {
|
|
|
|
if (data[i] !== 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
function isColEmpty(_a, x, top, bottom) {
|
|
|
|
var data = _a.data, width = _a.width;
|
|
|
|
var stride = (width * 4) | 0;
|
|
|
|
var start = (top * stride + x * 4 + 3) | 0;
|
|
|
|
for (var y = top, i = start; y < bottom; y++, i = (i + stride) | 0) {
|
|
|
|
if (data[i] !== 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
function trimData(data) {
|
|
|
|
var top = 0;
|
|
|
|
var left = 0;
|
|
|
|
var right = data.width;
|
|
|
|
var bottom = data.height;
|
|
|
|
while (top < bottom && isRowEmpty(data, top, left, right))
|
|
|
|
top++;
|
|
|
|
while (bottom > top && isRowEmpty(data, bottom - 1, left, right))
|
|
|
|
bottom--;
|
|
|
|
while (left < right && isColEmpty(data, left, top, bottom))
|
|
|
|
left++;
|
|
|
|
while (right > left && isColEmpty(data, right - 1, top, bottom))
|
|
|
|
right--;
|
|
|
|
return { top: top, left: left, right: right, bottom: bottom };
|
|
|
|
}
|
|
|
|
export function writeColor(writer, color) {
|
|
|
|
if (!color) {
|
|
|
|
writeUint16(writer, 0 /* ColorSpace.RGB */);
|
|
|
|
writeZeros(writer, 8);
|
|
|
|
}
|
|
|
|
else if ('r' in color) {
|
|
|
|
writeUint16(writer, 0 /* ColorSpace.RGB */);
|
|
|
|
writeUint16(writer, Math.round(color.r * 257));
|
|
|
|
writeUint16(writer, Math.round(color.g * 257));
|
|
|
|
writeUint16(writer, Math.round(color.b * 257));
|
|
|
|
writeUint16(writer, 0);
|
|
|
|
}
|
|
|
|
else if ('l' in color) {
|
|
|
|
writeUint16(writer, 7 /* ColorSpace.Lab */);
|
|
|
|
writeInt16(writer, Math.round(color.l * 10000));
|
|
|
|
writeInt16(writer, Math.round(color.a < 0 ? (color.a * 12800) : (color.a * 12700)));
|
|
|
|
writeInt16(writer, Math.round(color.b < 0 ? (color.b * 12800) : (color.b * 12700)));
|
|
|
|
writeUint16(writer, 0);
|
|
|
|
}
|
|
|
|
else if ('h' in color) {
|
|
|
|
writeUint16(writer, 1 /* ColorSpace.HSB */);
|
|
|
|
writeUint16(writer, Math.round(color.h * 0xffff));
|
|
|
|
writeUint16(writer, Math.round(color.s * 0xffff));
|
|
|
|
writeUint16(writer, Math.round(color.b * 0xffff));
|
|
|
|
writeUint16(writer, 0);
|
|
|
|
}
|
|
|
|
else if ('c' in color) {
|
|
|
|
writeUint16(writer, 2 /* ColorSpace.CMYK */);
|
|
|
|
writeUint16(writer, Math.round(color.c * 257));
|
|
|
|
writeUint16(writer, Math.round(color.m * 257));
|
|
|
|
writeUint16(writer, Math.round(color.y * 257));
|
|
|
|
writeUint16(writer, Math.round(color.k * 257));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
writeUint16(writer, 8 /* ColorSpace.Grayscale */);
|
|
|
|
writeUint16(writer, Math.round(color.k * 10000 / 255));
|
|
|
|
writeZeros(writer, 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//# sourceMappingURL=data:application/json;charset=utf8;base64,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
|