2023-07-27 07:04:13 +00:00
|
|
|
import { BlnM, parseAngle, parsePercent, parseUnitsToNumber, readVersionAndDescriptor } from './descriptor';
|
|
|
|
import { checkSignature, createReader, readBytes, readDataRLE, readInt16, readInt32, readPascalString, readPattern, readSignature, readUint16, readUint32, readUint8, skipBytes } from './psdReader';
|
|
|
|
var dynamicsControl = ['off', 'fade', 'pen pressure', 'pen tilt', 'stylus wheel', 'initial direction', 'direction', 'initial rotation', 'rotation'];
|
|
|
|
function parseDynamics(desc) {
|
|
|
|
return {
|
|
|
|
control: dynamicsControl[desc.bVTy],
|
|
|
|
steps: desc.fStp,
|
|
|
|
jitter: parsePercent(desc.jitter),
|
|
|
|
minimum: parsePercent(desc['Mnm ']),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
function parseBrushShape(desc) {
|
|
|
|
var shape = {
|
|
|
|
size: parseUnitsToNumber(desc.Dmtr, 'Pixels'),
|
|
|
|
angle: parseAngle(desc.Angl),
|
|
|
|
roundness: parsePercent(desc.Rndn),
|
|
|
|
spacingOn: desc.Intr,
|
|
|
|
spacing: parsePercent(desc.Spcn),
|
|
|
|
flipX: desc.flipX,
|
|
|
|
flipY: desc.flipY,
|
|
|
|
};
|
|
|
|
if (desc['Nm '])
|
|
|
|
shape.name = desc['Nm '];
|
|
|
|
if (desc.Hrdn)
|
|
|
|
shape.hardness = parsePercent(desc.Hrdn);
|
|
|
|
if (desc.sampledData)
|
|
|
|
shape.sampledData = desc.sampledData;
|
|
|
|
return shape;
|
|
|
|
}
|
|
|
|
export function readAbr(buffer, options) {
|
|
|
|
var _a, _b, _c, _d;
|
|
|
|
if (options === void 0) { options = {}; }
|
|
|
|
var reader = createReader(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
|
|
var version = readInt16(reader);
|
|
|
|
var samples = [];
|
|
|
|
var brushes = [];
|
|
|
|
var patterns = [];
|
|
|
|
if (version === 1 || version === 2) {
|
|
|
|
throw new Error("Unsupported ABR version (".concat(version, ")")); // TODO: ...
|
|
|
|
}
|
|
|
|
else if (version === 6 || version === 7 || version === 9 || version === 10) {
|
|
|
|
var minorVersion = readInt16(reader);
|
|
|
|
if (minorVersion !== 1 && minorVersion !== 2)
|
|
|
|
throw new Error('Unsupported ABR minor version');
|
|
|
|
while (reader.offset < reader.view.byteLength) {
|
|
|
|
checkSignature(reader, '8BIM');
|
|
|
|
var type = readSignature(reader);
|
|
|
|
var size = readUint32(reader);
|
|
|
|
var end = reader.offset + size;
|
|
|
|
switch (type) {
|
|
|
|
case 'samp': {
|
|
|
|
while (reader.offset < end) {
|
|
|
|
var brushLength = readUint32(reader);
|
|
|
|
while (brushLength & 3)
|
|
|
|
brushLength++; // pad to 4 byte alignment
|
|
|
|
var brushEnd = reader.offset + brushLength;
|
|
|
|
var id = readPascalString(reader, 1);
|
|
|
|
// v1 - Skip the Int16 bounds rectangle and the unknown Int16.
|
|
|
|
// v2 - Skip the unknown bytes.
|
|
|
|
skipBytes(reader, minorVersion === 1 ? 10 : 264);
|
|
|
|
var y = readInt32(reader);
|
|
|
|
var x = readInt32(reader);
|
|
|
|
var h = readInt32(reader) - y;
|
|
|
|
var w = readInt32(reader) - x;
|
|
|
|
if (w <= 0 || h <= 0)
|
|
|
|
throw new Error('Invalid bounds');
|
|
|
|
var depth = readInt16(reader);
|
|
|
|
var compression = readUint8(reader); // 0 - raw, 1 - RLE
|
|
|
|
var alpha = new Uint8Array(w * h);
|
|
|
|
if (depth === 8) {
|
|
|
|
if (compression === 0) {
|
|
|
|
alpha.set(readBytes(reader, alpha.byteLength));
|
|
|
|
}
|
|
|
|
else if (compression === 1) {
|
|
|
|
readDataRLE(reader, { width: w, height: h, data: alpha }, w, h, 1, [0], false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw new Error('Invalid compression');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (depth === 16) {
|
|
|
|
if (compression === 0) {
|
|
|
|
for (var i = 0; i < alpha.byteLength; i++) {
|
|
|
|
alpha[i] = readUint16(reader) >> 8; // convert to 8bit values
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (compression === 1) {
|
|
|
|
throw new Error('not implemented (16bit RLE)'); // TODO: ...
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw new Error('Invalid compression');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw new Error('Invalid depth');
|
|
|
|
}
|
|
|
|
samples.push({ id: id, bounds: { x: x, y: y, w: w, h: h }, alpha: alpha });
|
|
|
|
reader.offset = brushEnd;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'desc': {
|
|
|
|
var desc = readVersionAndDescriptor(reader);
|
|
|
|
// console.log(require('util').inspect(desc, false, 99, true));
|
|
|
|
for (var _i = 0, _e = desc.Brsh; _i < _e.length; _i++) {
|
|
|
|
var brush = _e[_i];
|
|
|
|
var b = {
|
|
|
|
name: brush['Nm '],
|
|
|
|
shape: parseBrushShape(brush.Brsh),
|
|
|
|
spacing: parsePercent(brush.Spcn),
|
|
|
|
// TODO: brushGroup ???
|
|
|
|
wetEdges: brush.Wtdg,
|
|
|
|
noise: brush.Nose,
|
|
|
|
// TODO: TxtC ??? smoothing / build-up ?
|
|
|
|
// TODO: 'Rpt ' ???
|
|
|
|
useBrushSize: brush.useBrushSize, // ???
|
|
|
|
};
|
|
|
|
if (brush.interpretation != null)
|
|
|
|
b.interpretation = brush.interpretation;
|
|
|
|
if (brush.protectTexture != null)
|
|
|
|
b.protectTexture = brush.protectTexture;
|
|
|
|
if (brush.useTipDynamics) {
|
|
|
|
b.shapeDynamics = {
|
|
|
|
tiltScale: parsePercent(brush.tiltScale),
|
|
|
|
sizeDynamics: parseDynamics(brush.szVr),
|
|
|
|
angleDynamics: parseDynamics(brush.angleDynamics),
|
|
|
|
roundnessDynamics: parseDynamics(brush.roundnessDynamics),
|
|
|
|
flipX: brush.flipX,
|
|
|
|
flipY: brush.flipY,
|
|
|
|
brushProjection: brush.brushProjection,
|
|
|
|
minimumDiameter: parsePercent(brush.minimumDiameter),
|
|
|
|
minimumRoundness: parsePercent(brush.minimumRoundness),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (brush.useScatter) {
|
|
|
|
b.scatter = {
|
|
|
|
count: brush['Cnt '],
|
|
|
|
bothAxes: brush.bothAxes,
|
|
|
|
countDynamics: parseDynamics(brush.countDynamics),
|
|
|
|
scatterDynamics: parseDynamics(brush.scatterDynamics),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (brush.useTexture && brush.Txtr) {
|
|
|
|
b.texture = {
|
|
|
|
id: brush.Txtr.Idnt,
|
|
|
|
name: brush.Txtr['Nm '],
|
|
|
|
blendMode: BlnM.decode(brush.textureBlendMode),
|
|
|
|
depth: parsePercent(brush.textureDepth),
|
|
|
|
depthMinimum: parsePercent(brush.minimumDepth),
|
|
|
|
depthDynamics: parseDynamics(brush.textureDepthDynamics),
|
|
|
|
scale: parsePercent(brush.textureScale),
|
|
|
|
invert: brush.InvT,
|
|
|
|
brightness: brush.textureBrightness,
|
|
|
|
contrast: brush.textureContrast,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
var db = brush.dualBrush;
|
|
|
|
if (db && db.useDualBrush) {
|
|
|
|
b.dualBrush = {
|
|
|
|
flip: db.Flip,
|
|
|
|
shape: parseBrushShape(db.Brsh),
|
|
|
|
blendMode: BlnM.decode(db.BlnM),
|
|
|
|
useScatter: db.useScatter,
|
|
|
|
spacing: parsePercent(db.Spcn),
|
|
|
|
count: db['Cnt '],
|
|
|
|
bothAxes: db.bothAxes,
|
|
|
|
countDynamics: parseDynamics(db.countDynamics),
|
|
|
|
scatterDynamics: parseDynamics(db.scatterDynamics),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (brush.useColorDynamics) {
|
|
|
|
b.colorDynamics = {
|
|
|
|
foregroundBackground: parseDynamics(brush.clVr),
|
|
|
|
hue: parsePercent(brush['H ']),
|
|
|
|
saturation: parsePercent(brush.Strt),
|
|
|
|
brightness: parsePercent(brush.Brgh),
|
|
|
|
purity: parsePercent(brush.purity),
|
|
|
|
perTip: brush.colorDynamicsPerTip,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (brush.usePaintDynamics) {
|
|
|
|
b.transfer = {
|
|
|
|
flowDynamics: parseDynamics(brush.prVr),
|
|
|
|
opacityDynamics: parseDynamics(brush.opVr),
|
|
|
|
wetnessDynamics: parseDynamics(brush.wtVr),
|
|
|
|
mixDynamics: parseDynamics(brush.mxVr),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (brush.useBrushPose) {
|
|
|
|
b.brushPose = {
|
|
|
|
overrideAngle: brush.overridePoseAngle,
|
|
|
|
overrideTiltX: brush.overridePoseTiltX,
|
|
|
|
overrideTiltY: brush.overridePoseTiltY,
|
|
|
|
overridePressure: brush.overridePosePressure,
|
|
|
|
pressure: parsePercent(brush.brushPosePressure),
|
|
|
|
tiltX: brush.brushPoseTiltX,
|
|
|
|
tiltY: brush.brushPoseTiltY,
|
|
|
|
angle: brush.brushPoseAngle,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
var to = brush.toolOptions;
|
|
|
|
if (to) {
|
|
|
|
b.toolOptions = {
|
|
|
|
brushPreset: to.brushPreset,
|
|
|
|
flow: (_a = to.flow) !== null && _a !== void 0 ? _a : 100,
|
|
|
|
smooth: (_b = to.Smoo) !== null && _b !== void 0 ? _b : 0,
|
|
|
|
mode: BlnM.decode(to['Md '] || 'BlnM.Nrml'),
|
|
|
|
opacity: (_c = to.Opct) !== null && _c !== void 0 ? _c : 100,
|
|
|
|
smoothing: !!to.smoothing,
|
|
|
|
smoothingValue: to.smoothingValue || 0,
|
|
|
|
smoothingRadiusMode: !!to.smoothingRadiusMode,
|
|
|
|
smoothingCatchup: !!to.smoothingCatchup,
|
|
|
|
smoothingCatchupAtEnd: !!to.smoothingCatchupAtEnd,
|
|
|
|
smoothingZoomCompensation: !!to.smoothingZoomCompensation,
|
|
|
|
pressureSmoothing: !!to.pressureSmoothing,
|
|
|
|
usePressureOverridesSize: !!to.usePressureOverridesSize,
|
|
|
|
usePressureOverridesOpacity: !!to.usePressureOverridesOpacity,
|
|
|
|
useLegacy: !!to.useLegacy,
|
|
|
|
};
|
|
|
|
if (to.prVr) {
|
|
|
|
b.toolOptions.flowDynamics = parseDynamics(to.prVr);
|
|
|
|
}
|
|
|
|
if (to.opVr) {
|
|
|
|
b.toolOptions.opacityDynamics = parseDynamics(to.opVr);
|
|
|
|
}
|
|
|
|
if (to.szVr) {
|
|
|
|
b.toolOptions.sizeDynamics = parseDynamics(to.szVr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
brushes.push(b);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'patt': {
|
|
|
|
if (reader.offset < end) { // TODO: check multiple patterns
|
|
|
|
patterns.push(readPattern(reader));
|
|
|
|
reader.offset = end;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'phry': {
|
|
|
|
// TODO: what is this ?
|
|
|
|
var desc = readVersionAndDescriptor(reader);
|
|
|
|
if (options.logMissingFeatures) {
|
|
|
|
if ((_d = desc.hierarchy) === null || _d === void 0 ? void 0 : _d.length) {
|
|
|
|
console.log('unhandled phry section', desc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new Error("Invalid brush type: ".concat(type));
|
|
|
|
}
|
|
|
|
// align to 4 bytes
|
|
|
|
while (size % 4) {
|
|
|
|
reader.offset++;
|
|
|
|
size++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw new Error("Unsupported ABR version (".concat(version, ")"));
|
|
|
|
}
|
|
|
|
return { samples: samples, patterns: patterns, brushes: brushes };
|
|
|
|
}
|
|
|
|
|
|
|
|
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImFici50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsSUFBSSxFQUF3QixVQUFVLEVBQUUsWUFBWSxFQUFFLGtCQUFrQixFQUFFLHdCQUF3QixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBRWxJLE9BQU8sRUFDTixjQUFjLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSxXQUFXLEVBQ3pHLGFBQWEsRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQzNELE1BQU0sYUFBYSxDQUFDO0FBcUJyQixJQUFNLGVBQWUsR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUUsbUJBQW1CLEVBQUUsV0FBVyxFQUFFLGtCQUFrQixFQUFFLFVBQVUsQ0FBQyxDQUFDO0FBa1B0SixTQUFTLGFBQWEsQ0FBQyxJQUF3QjtJQUM5QyxPQUFPO1FBQ04sT0FBTyxFQUFFLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFRO1FBQzFDLEtBQUssRUFBRSxJQUFJLENBQUMsSUFBSTtRQUNoQixNQUFNLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDakMsT0FBTyxFQUFFLFlBQVksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7S0FDbkMsQ0FBQztBQUNILENBQUM7QUFFRCxTQUFTLGVBQWUsQ0FBQyxJQUEwQjtJQUNsRCxJQUFNLEtBQUssR0FBZTtRQUN6QixJQUFJLEVBQUUsa0JBQWtCLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUM7UUFDN0MsS0FBSyxFQUFFLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQzVCLFNBQVMsRUFBRSxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQztRQUNsQyxTQUFTLEVBQUUsSUFBSSxDQUFDLElBQUk7UUFDcEIsT0FBTyxFQUFFLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQ2hDLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztRQUNqQixLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUs7S0FDakIsQ0FBQztJQUVGLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUFFLEtBQUssQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzVDLElBQUksSUFBSSxDQUFDLElBQUk7UUFBRSxLQUFLLENBQUMsUUFBUSxHQUFHLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEQsSUFBSSxJQUFJLENBQUMsV0FBVztRQUFFLEtBQUssQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQztJQUUzRCxPQUFPLEtBQUssQ0FBQztBQUNkLENBQUM7QUFFRCxNQUFNLFVBQVUsT0FBTyxDQUFDLE1BQXVCLEVBQUUsT0FBK0M7O0lBQS9DLHdCQUFBLEVBQUEsWUFBK0M7SUFDL0YsSUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLFVBQVUsRUFBRSxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDakYsSUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2xDLElBQU0sT0FBTyxHQUFpQixFQUFFLENBQUM7SUFDakMsSUFBTSxPQUFPLEdBQVksRUFBRSxDQUFDO0lBQzVCLElBQU0sUUFBUSxHQUFrQixFQUFFLENBQUM7SUFFbkMsSUFBSSxPQUFPLEtBQUssQ0FBQyxJQUFJLE9BQU8sS0FBSyxDQUFDLEVBQUU7UUFDbkMsTUFBTSxJQUFJLEtBQUssQ0FBQyxtQ0FBNEIsT0FBTyxNQUFHLENBQUMsQ0FBQyxDQUFDLFlBQVk7S0FDckU7U0FBTSxJQUFJLE9BQU8sS0FBSyxDQUFDLElBQUksT0FBTyxLQUFLLENBQUMsSUFBSSxPQUFPLEtBQUssQ0FBQyxJQUFJLE9BQU8sS0FBSyxFQUFFLEVBQUU7UUFDN0UsSUFBTSxZQUFZLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3ZDLElBQUksWUFBWSxLQUFLLENBQUMsSUFBSSxZQUFZLEtBQUssQ0FBQztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsK0JBQStCLENBQUMsQ0FBQztRQUUvRixPQUFPLE1BQU0sQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUU7WUFDOUMsY0FBYyxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUMvQixJQUFNLElBQUksR0FBRyxhQUFhLENBQUMsTUFBTSxDQUFzQyxDQUFDO1lBQ3hFLElBQUksSUFBSSxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUM5QixJQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztZQUVqQyxRQUFRLElBQUksRUFBRTtnQkFDYixLQUFLLE1BQU0sQ0FBQyxDQUFDO29CQUNaLE9BQU8sTUFBTSxDQUFDLE1BQU0sR0FBRyxHQUFHLEVBQUU7d0JBQzNCLElBQUksV0FBVyxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQzt3QkFDckMsT0FBTyxXQUFXLEdBQUcsQ0FBSTs0QkFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDLDBCQUEwQjt3QkFDcEUsSUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLE1BQU0sR0FBRyxXQUFXLENBQUM7d0JBRTdDLElBQU0sRUFBRSxHQUFHLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQzt3QkFFdkMsOERBQThEO3dCQUM5RCwrQkFBK0I7d0JBQy9CLFNBQVMsQ0FBQyxNQUFNLEVBQUUsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQzt3QkFFakQsSUFBTSxDQUFDLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO3dCQUM1QixJQUFNLENBQUMsR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7d0JBQzVCLElBQU0sQ0FBQyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7d0JBQ2hDLElBQU0sQ0FBQyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7d0JBQ2hDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQzs0QkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUM7d0JBRXhELElBQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQzt3QkFDaEMsSUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsbUJBQW1CO3dCQUMxRCxJQUFNLEtBQUssR0FB
|