From 93b99e73521ce58a46fbbea5c9f8d408658cc183 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Thu, 14 Mar 2013 14:06:44 -0700 Subject: [PATCH] Fix where image component decoding takes place. --- src/image.js | 93 ++++++++++++++++++++---------------- test/pdfs/issue2770.pdf.link | 1 + test/test_manifest.json | 8 ++++ 3 files changed, 61 insertions(+), 41 deletions(-) create mode 100644 test/pdfs/issue2770.pdf.link diff --git a/src/image.js b/src/image.js index 48f174c76..84286d831 100644 --- a/src/image.js +++ b/src/image.js @@ -49,7 +49,7 @@ var PDFImage = (function PDFImageClosure() { // Clamp the value to the range return value < 0 ? 0 : value > max ? max : value; } - function PDFImage(xref, res, image, inline, smask, mask) { + function PDFImage(xref, res, image, inline, smask, mask, isMask) { this.image = image; if (image.getParams) { // JPX/JPEG2000 streams directly contain bits per component @@ -95,8 +95,9 @@ var PDFImage = (function PDFImageClosure() { this.decode = dict.get('Decode', 'D'); this.needsDecode = false; - if (this.decode && this.colorSpace && - !this.colorSpace.isDefaultDecode(this.decode)) { + if (this.decode && + ((this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode)) || + (isMask && !ColorSpace.isDefaultDecode(this.decode, 1)))) { this.needsDecode = true; // Do some preprocessing to avoid more math. var max = (1 << bitsPerComponent) - 1; @@ -114,7 +115,7 @@ var PDFImage = (function PDFImageClosure() { this.smask = new PDFImage(xref, res, smask, false); } else if (mask) { if (isStream(mask)) { - this.mask = new PDFImage(xref, res, mask, false); + this.mask = new PDFImage(xref, res, mask, false, null, null, true); } else { // Color key mask (just an array). this.mask = mask; @@ -216,13 +217,37 @@ var PDFImage = (function PDFImageClosure() { return this.height; return Math.max(this.height, this.smask.height); }, + decodeBuffer: function PDFImage_decodeBuffer(buffer) { + var bpc = this.bpc; + var decodeMap = this.decode; + var numComps = this.numComps; + + var decodeAddends, decodeCoefficients; + var decodeAddends = this.decodeAddends; + var decodeCoefficients = this.decodeCoefficients; + var max = (1 << bpc) - 1; + + if (bpc === 1) { + // If the buffer needed decode that means it just needs to be inverted. + for (var i = 0, ii = buffer.length; i < ii; i++) { + buffer[i] = +!(buffer[i]); + } + return; + } + var index = 0; + for (var i = 0, ii = this.width * this.height; i < ii; i++) { + for (var j = 0; j < numComps; j++) { + buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j], + decodeCoefficients[j], max); + index++; + } + } + }, getComponents: function PDFImage_getComponents(buffer) { var bpc = this.bpc; - var needsDecode = this.needsDecode; - var decodeMap = this.decode; // This image doesn't require any extra work. - if (bpc == 8 && !needsDecode) + if (bpc === 8) return buffer; var bufferLength = buffer.length; @@ -235,29 +260,11 @@ var PDFImage = (function PDFImageClosure() { var output = bpc <= 8 ? new Uint8Array(length) : bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length); var rowComps = width * numComps; - var decodeAddends, decodeCoefficients; - if (needsDecode) { - decodeAddends = this.decodeAddends; - decodeCoefficients = this.decodeCoefficients; - } + var max = (1 << bpc) - 1; - if (bpc == 8) { - // Optimization for reading 8 bpc images that have a decode. - for (var i = 0, ii = length; i < ii; ++i) { - var compIndex = i % numComps; - var value = buffer[i]; - value = decodeAndClamp(value, decodeAddends[compIndex], - decodeCoefficients[compIndex], max); - output[i] = value; - } - } else if (bpc == 1) { + if (bpc === 1) { // Optimization for reading 1 bpc images. - var valueZero = 0, valueOne = 1; - if (decodeMap) { - valueZero = decodeMap[0] ? 1 : 0; - valueOne = decodeMap[1] ? 1 : 0; - } var mask = 0; var buf = 0; @@ -274,7 +281,7 @@ var PDFImage = (function PDFImageClosure() { mask = 128; } - output[i] = !(buf & mask) ? valueZero : valueOne; + output[i] = +!!(buf & mask); } } else { // The general case that handles all other bpc values. @@ -292,12 +299,7 @@ var PDFImage = (function PDFImageClosure() { var remainingBits = bits - bpc; var value = buf >> remainingBits; - if (needsDecode) { - var compIndex = i % numComps; - value = decodeAndClamp(value, decodeAddends[compIndex], - decodeCoefficients[compIndex], max); - } - output[i] = value; + output[i] = value < 0 ? 0 : value > max ? max : value; buf = buf & ((1 << remainingBits) - 1); bits = remainingBits; } @@ -397,21 +399,27 @@ var PDFImage = (function PDFImageClosure() { // imgArray can be incomplete (e.g. after CCITT fax encoding) var actualHeight = 0 | (imgArray.length / rowBytes * height / originalHeight); + var comps = this.getComponents(imgArray); + // Build opacity here since color key masking needs to be perormed on + // undecoded values. + var opacity = this.getOpacity(width, height, comps); - var comps = this.colorSpace.createRgbBuffer(this.getComponents(imgArray), - 0, originalWidth * originalHeight, bpc); + if (this.needsDecode) { + this.decodeBuffer(comps); + } + var rgbBuf = this.colorSpace.createRgbBuffer(comps, 0, + originalWidth * originalHeight, bpc); if (originalWidth != width || originalHeight != height) - comps = PDFImage.resize(comps, this.bpc, 3, originalWidth, + rgbBuf = PDFImage.resize(rgbBuf, this.bpc, 3, originalWidth, originalHeight, width, height); var compsPos = 0; - var opacity = this.getOpacity(width, height, imgArray); var opacityPos = 0; var length = width * actualHeight * 4; for (var i = 0; i < length; i += 4) { - buffer[i] = comps[compsPos++]; - buffer[i + 1] = comps[compsPos++]; - buffer[i + 2] = comps[compsPos++]; + buffer[i] = rgbBuf[compsPos++]; + buffer[i + 1] = rgbBuf[compsPos++]; + buffer[i + 2] = rgbBuf[compsPos++]; buffer[i + 3] = opacity[opacityPos++]; } }, @@ -429,6 +437,9 @@ var PDFImage = (function PDFImageClosure() { var imgArray = this.getImageBytes(height * rowBytes); var comps = this.getComponents(imgArray); + if (this.needsDecode) { + this.decodeBuffer(comps); + } var length = width * height; // we aren't using a colorspace so we need to scale the value var scale = 255 / ((1 << bpc) - 1); diff --git a/test/pdfs/issue2770.pdf.link b/test/pdfs/issue2770.pdf.link new file mode 100644 index 000000000..42326a258 --- /dev/null +++ b/test/pdfs/issue2770.pdf.link @@ -0,0 +1 @@ +https://bugzilla.mozilla.org/attachment.cgi?id=718826 diff --git a/test/test_manifest.json b/test/test_manifest.json index ec626749f..cbd885fdc 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -971,6 +971,14 @@ "link": true, "type": "eq" }, + { "id": "issue2770", + "file": "pdfs/issue2770.pdf", + "md5": "36070d756d06eaa35c2227efb069fb1b", + "rounds": 1, + "link": true, + "type": "eq", + "about": "Has a 4 bit per component image with mask and decode." + }, { "id": "p020121130574743273239", "file": "pdfs/P020121130574743273239.pdf", "md5": "271b65885d42d174cbc597ca89becb1a",