Skip to content

Commit 8da9b58

Browse files
committed
feat: add support for CMYK color mode
1 parent 379759b commit 8da9b58

File tree

2 files changed

+26
-18
lines changed

2 files changed

+26
-18
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@
1414
.v8-cache
1515
TODO.md
1616
.idea
17+
18+
.DS_Store

src/psdReader.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface ChannelInfo {
99
length: number;
1010
}
1111

12-
export const supportedColorModes = [ColorMode.Bitmap, ColorMode.Grayscale, ColorMode.RGB, ColorMode.Indexed];
12+
export const supportedColorModes = [ColorMode.Bitmap, ColorMode.Grayscale, ColorMode.RGB, ColorMode.Indexed, ColorMode.CMYK];
1313
const colorModes = ['bitmap', 'grayscale', 'indexed', 'RGB', 'CMYK', 'multichannel', 'duotone', 'lab'];
1414

1515
function setupGrayscale(data: PixelData) {
@@ -635,7 +635,7 @@ function readLayerChannelImageData(reader: PsdReader, psd: Psd, layer: Layer, ch
635635
if (cmyk) {
636636
const cmykData = imageData;
637637
imageData = createImageData(cmykData.width, cmykData.height);
638-
cmykToRgb(cmykData, imageData, false);
638+
cmykToRgb(cmykData, imageData);
639639
}
640640

641641
if (reader.useImageData) {
@@ -802,10 +802,14 @@ function readImageData(reader: PsdReader, psd: Psd) {
802802
break;
803803
}
804804
case ColorMode.CMYK: {
805-
if (bitsPerChannel !== 8) throw new Error('bitsPerChannel Not supproted');
806-
if (psd.channels !== 4) throw new Error(`Invalid channel count`);
805+
if (bitsPerChannel !== 8) throw new Error('bitsPerChannel Not supported');
806+
// there is additional color channel for cmyk when channels === 5
807+
if (psd.channels !== 4 && psd.channels !== 5) throw new Error(`Invalid channel count`);
808+
// there is additional alpha channel for cmyk
809+
const channelLen = psd.channels! + 1;
807810

808811
const channels = [0, 1, 2, 3];
812+
const haveAlpha = reader.globalAlpha || psd.channels === 5;
809813
if (reader.globalAlpha) channels.push(4);
810814

811815
if (compression === Compression.RawData) {
@@ -818,12 +822,12 @@ function readImageData(reader: PsdReader, psd: Psd) {
818822
const cmykImageData: PixelData = {
819823
width: imageData.width,
820824
height: imageData.height,
821-
data: new Uint8Array(imageData.width * imageData.height * 5),
825+
data: new Uint8Array(imageData.width * imageData.height * channelLen),
822826
};
823827

824828
const start = reader.offset;
825-
readDataRLE(reader, cmykImageData, psd.width, psd.height, bitsPerChannel, 5, channels, reader.large);
826-
cmykToRgb(cmykImageData, imageData, true);
829+
readDataRLE(reader, cmykImageData, psd.width, psd.height, bitsPerChannel, channelLen, channels, reader.large);
830+
cmykToRgb(cmykImageData, imageData, !haveAlpha, channelLen);
827831

828832
if (RAW_IMAGE_DATA) (psd as any).imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
829833
} else {
@@ -860,20 +864,21 @@ function readImageData(reader: PsdReader, psd: Psd) {
860864
}
861865
}
862866

863-
function cmykToRgb(cmyk: PixelData, rgb: PixelData, reverseAlpha: boolean) {
867+
function cmykToRgb(cmyk: PixelData, rgb: PixelData, reverseAlpha = false, channelLen = 5) {
864868
const size = rgb.width * rgb.height * 4;
865869
const srcData = cmyk.data;
866870
const dstData = rgb.data;
867871

868-
for (let src = 0, dst = 0; dst < size; src += 5, dst += 4) {
869-
const c = srcData[src];
870-
const m = srcData[src + 1];
871-
const y = srcData[src + 2];
872-
const k = srcData[src + 3];
873-
dstData[dst] = ((((c * k) | 0) / 255) | 0);
874-
dstData[dst + 1] = ((((m * k) | 0) / 255) | 0);
875-
dstData[dst + 2] = ((((y * k) | 0) / 255) | 0);
876-
dstData[dst + 3] = reverseAlpha ? 255 - srcData[src + 4] : srcData[src + 4];
872+
for (let src = 0, dst = 0; dst < size; src += channelLen, dst += 4) {
873+
const c = 255 - srcData[src + 0];
874+
const m = 255 - srcData[src + 1];
875+
const y = 255 - srcData[src + 2];
876+
const k = 255 - srcData[src + 3];
877+
const a = srcData[src + 4];
878+
dstData[dst + 0] = ((65535 - (c * (255 - k) + (k << 8))) >> 8) | 0;
879+
dstData[dst + 1] = ((65535 - (m * (255 - k) + (k << 8))) >> 8) | 0;
880+
dstData[dst + 2] = ((65535 - (y * (255 - k) + (k << 8))) >> 8) | 0;
881+
dstData[dst + 3] = reverseAlpha ? 255 - a : a;
877882
}
878883

879884
// for (let src = 0, dst = 0; dst < size; src += 5, dst += 4) {
@@ -1033,7 +1038,8 @@ export function readDataRLE(reader: PsdReader, pixelData: PixelData | undefined,
10331038

10341039
if (bitDepth !== 1 && bitDepth !== 8) throw new Error(`Invalid bit depth (${bitDepth})`);
10351040

1036-
const extraLimit = (step - 1) | 0; // 3 for rgb, 4 for cmyk
1041+
const extraLimit = (step - 1) | 0; // 3 for rgb, 4 for cmyk, 5 for cmyk+extraColor
1042+
10371043

10381044
for (let c = 0, li = 0; c < offsets.length; c++) {
10391045
const offset = offsets[c] | 0;

0 commit comments

Comments
 (0)