From f62c1c469fb31e113469d3af38b2c1818748e085 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 24 Feb 2014 19:37:19 -0800 Subject: [PATCH] Special-case 24-bit RGB image-handling. --- src/core/evaluator.js | 6 +++-- src/core/image.js | 52 ++++++++++++++++++++++++++----------------- src/display/canvas.js | 16 +++++++++++++ 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 2e85cbd16..2f76c26c7 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -166,7 +166,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { (w + h) < SMALL_IMAGE_DIMENSIONS) { var imageObj = new PDFImage(this.xref, resources, image, inline, null, null); - var imgData = imageObj.createImageData(); + // We force the use of 'rgba_32bpp' images here, because we can't + // handle any other kind. + var imgData = imageObj.createImageData(/* forceRGBA = */ true); operatorList.addOp(OPS.paintInlineImageXObject, [imgData]); return; } @@ -189,7 +191,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { PDFImage.buildImage(function(imageObj) { - var imgData = imageObj.createImageData(); + var imgData = imageObj.createImageData(/* forceRGBA = */ false); self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData], null, [imgData.data.buffer]); }, self.handler, self.xref, resources, image, inline); diff --git a/src/core/image.js b/src/core/image.js index 1adad5e29..0655f87fe 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -417,7 +417,7 @@ var PDFImage = (function PDFImageClosure() { buffer[i + 2] = clamp((buffer[i + 2] - matteRgb[2]) * k + matteRgb[2]); } }, - createImageData: function PDFImage_createImageData() { + createImageData: function PDFImage_createImageData(forceRGBA) { var drawWidth = this.drawWidth; var drawHeight = this.drawHeight; var imgData = { // other fields are filled in below @@ -430,32 +430,42 @@ var PDFImage = (function PDFImageClosure() { var originalHeight = this.height; var bpc = this.bpc; - // rows start at byte boundary; + // Rows start at byte boundary. var rowBytes = (originalWidth * numComps * bpc + 7) >> 3; var imgArray = this.getImageBytes(originalHeight * rowBytes); - // imgArray can be incomplete (e.g. after CCITT fax encoding) + if (!forceRGBA) { + // If it is a 1-bit-per-pixel grayscale (i.e. black-and-white) image + // without any complications, we pass a same-sized copy to the main + // thread rather than expanding by 32x to RGBA form. This saves *lots* + // of memory for many scanned documents. It's also much faster. + // + // Similarly, if it is a 24-bit-per pixel RGB image without any + // complications, we avoid expanding by 1.333x to RGBA form. + var kind; + if (this.colorSpace.name === 'DeviceGray' && bpc === 1) { + kind = 'grayscale_1bpp'; + } else if (this.colorSpace.name === 'DeviceRGB' && bpc === 8) { + kind = 'rgb_24bpp'; + } + if (kind && !this.smask && !this.mask && !this.needsDecode && + drawWidth === originalWidth && drawHeight === originalHeight) { + imgData.kind = kind; + + // We must make a copy of imgArray, otherwise it'll be neutered upon + // transfer which will break any code that subsequently reuses it. + var newArray = new Uint8Array(imgArray.length); + newArray.set(imgArray); + imgData.data = newArray; + imgData.origLength = imgArray.length; + return imgData; + } + } + + // imgArray can be incomplete (e.g. after CCITT fax encoding). var actualHeight = 0 | (imgArray.length / rowBytes * drawHeight / originalHeight); - // If it is a 1-bit-per-pixel grayscale (i.e. black-and-white) image - // without any complications, we pass a same-sized copy to the main - // thread rather than expanding by 32x to RGBA form. This saves *lots* of - // memory for many scanned documents. It's also much faster. - if (this.colorSpace.name === 'DeviceGray' && bpc === 1 && - !this.smask && !this.mask && !this.needsDecode && - drawWidth === originalWidth && drawHeight === originalHeight) { - imgData.kind = 'grayscale_1bpp'; - - // We must make a copy of imgArray, otherwise it'll be neutered upon - // transfer which will break any code that subsequently reuses it. - var newArray = new Uint8Array(imgArray.length); - newArray.set(imgArray); - imgData.data = newArray; - imgData.origLength = imgArray.length; - return imgData; - } - var comps = this.getComponents(imgArray); var rgbaBuf = new Uint8Array(drawWidth * drawHeight * 4); diff --git a/src/display/canvas.js b/src/display/canvas.js index 7381aa691..5585cfa64 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -526,6 +526,22 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { ctx.putImageData(chunkImgData, 0, i * fullChunkHeight); } + } else if (imgData.kind === 'rgb_24bpp') { + // RGB, 24-bits per pixel. + for (var i = 0; i < totalChunks; i++) { + var thisChunkHeight = + (i < fullChunks) ? fullChunkHeight : partialChunkHeight; + var elemsInThisChunk = imgData.width * thisChunkHeight * 3; + var destPos = 0; + for (var j = 0; j < elemsInThisChunk; j += 3) { + chunkImgData.data[destPos++] = imgData.data[srcPos++]; + chunkImgData.data[destPos++] = imgData.data[srcPos++]; + chunkImgData.data[destPos++] = imgData.data[srcPos++]; + chunkImgData.data[destPos++] = 255; + } + ctx.putImageData(chunkImgData, 0, i * fullChunkHeight); + } + } else { error('bad image kind: ' + imgData.kind); }