Skip to content
This repository was archived by the owner on Apr 12, 2021. It is now read-only.

Commit b83371e

Browse files
authored
Merge pull request foliojs#893 from blikblum/fix-16bit-alpha
Fix 16bit png files with alpha channel
2 parents 2955161 + dfb5f93 commit b83371e

3 files changed

Lines changed: 51 additions & 7 deletions

File tree

lib/image/png.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@ class PNGImage {
1515
this.document = document;
1616
if (this.obj) { return; }
1717

18+
const hasAlphaChannel = this.image.hasAlphaChannel;
19+
1820
this.obj = this.document.ref({
1921
Type: 'XObject',
2022
Subtype: 'Image',
21-
BitsPerComponent: this.image.bits,
23+
BitsPerComponent: hasAlphaChannel ? 8 : this.image.bits,
2224
Width: this.width,
2325
Height: this.height,
2426
Filter: 'FlateDecode'
2527
});
2628

27-
if (!this.image.hasAlphaChannel) {
29+
if (!hasAlphaChannel) {
2830
const params = this.document.ref({
2931
Predictor: 15,
3032
Colors: this.image.colors,
@@ -71,7 +73,7 @@ class PNGImage {
7173
// in the PLTE and tRNS sections. See below for details on SMasks.
7274
return this.loadIndexedAlphaChannel();
7375

74-
} else if (this.image.hasAlphaChannel) {
76+
} else if (hasAlphaChannel) {
7577
// For PNG color types 4 and 6, the transparency data is stored as a alpha
7678
// channel mixed in with the main image data. Separate this data out into an
7779
// SMask object and store it separately in the PDF.
@@ -110,18 +112,21 @@ class PNGImage {
110112
return this.image.decodePixels(pixels => {
111113
let a, p;
112114
const colorCount = this.image.colors;
113-
const colorByteSize = (colorCount * this.image.bits) / 8;
114115
const pixelCount = this.width * this.height;
115-
const imgData = new Buffer(pixelCount * colorByteSize);
116-
const alphaChannel = new Buffer(pixelCount);
116+
const imgData = new Buffer(pixelCount * colorCount);
117+
const alphaChannel = new Buffer(pixelCount);
117118

118119
let i = p = a = 0;
119120
const len = pixels.length;
121+
// For 16bit images copy only most significant byte (MSB) - PNG data is always stored in network byte order (MSB first)
122+
const skipByteCount = this.image.bits === 16 ? 1 : 0;
120123
while (i < len) {
121124
for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) {
122125
imgData[p++] = pixels[i++];
126+
i += skipByteCount;
123127
}
124128
alphaChannel[a++] = pixels[i++];
129+
i += skipByteCount;
125130
}
126131

127132
this.imgData = zlib.deflateSync(imgData);

tests/images/straight.png

28 KB
Loading

tests/unit/png.spec.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ describe("PNGImage", () => {
9191
});
9292
});
9393

94-
test("RGB with Alpha", () => {
94+
test("RGB (8bit) with Alpha", () => {
9595
// ImageWidth = 409
9696
// ImageHeight = 400
9797
// BitDepth = 8
@@ -130,6 +130,45 @@ describe("PNGImage", () => {
130130
});
131131
});
132132

133+
test("RGB (16bit) with Alpha", () => {
134+
// ImageWidth = 175
135+
// ImageHeight = 65
136+
// BitDepth = 16
137+
// ColorType = 6
138+
// Compression = 0
139+
// Filter = 0
140+
// Interlace = 0
141+
142+
const img = createImage("./tests/images/straight.png");
143+
144+
expect(img.obj.data).toMatchObject({
145+
BitsPerComponent: 8,
146+
ColorSpace: "DeviceRGB",
147+
Filter: "FlateDecode",
148+
Height: 65,
149+
Length: 28537,
150+
Subtype: "Image",
151+
Type: "XObject",
152+
Width: 175,
153+
SMask: expect.any(PDFReference)
154+
});
155+
156+
expect(img.obj.data.SMask.data).toMatchObject({
157+
BitsPerComponent: 8,
158+
ColorSpace: "DeviceGray",
159+
Decode: [
160+
0,
161+
1
162+
],
163+
Filter: "FlateDecode",
164+
Height: 65,
165+
Length: 16,
166+
Subtype: "Image",
167+
Type: "XObject",
168+
Width: 175,
169+
});
170+
});
171+
133172
test("Pallete", () => {
134173
// ImageWidth = 980
135174
// ImageHeight = 540

0 commit comments

Comments
 (0)