Merge pull request #4336 from nnethercote/rgb24
Special-case 24-bit RGB image-handling
This commit is contained in:
commit
9a918572dd
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
/* globals assert, assertWellFormed, ColorSpace, Dict, Encodings, error,
|
/* globals assert, assertWellFormed, ColorSpace, Dict, Encodings, error,
|
||||||
ErrorFont, Font, FONT_IDENTITY_MATRIX, fontCharsToUnicode, FontFlags,
|
ErrorFont, Font, FONT_IDENTITY_MATRIX, fontCharsToUnicode, FontFlags,
|
||||||
info, isArray, isCmd, isDict, isEOF, isName, isNum,
|
ImageKind, info, isArray, isCmd, isDict, isEOF, isName, isNum,
|
||||||
isStream, isString, JpegStream, Lexer, Metrics, Name, Parser,
|
isStream, isString, JpegStream, Lexer, Metrics, Name, Parser,
|
||||||
Pattern, PDFImage, PDFJS, serifFonts, stdFontMap, symbolsFonts,
|
Pattern, PDFImage, PDFJS, serifFonts, stdFontMap, symbolsFonts,
|
||||||
getTilingPatternIR, warn, Util, Promise, LegacyPromise,
|
getTilingPatternIR, warn, Util, Promise, LegacyPromise,
|
||||||
@ -164,7 +164,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
(w + h) < SMALL_IMAGE_DIMENSIONS) {
|
(w + h) < SMALL_IMAGE_DIMENSIONS) {
|
||||||
var imageObj = new PDFImage(this.xref, resources, image,
|
var imageObj = new PDFImage(this.xref, resources, image,
|
||||||
inline, null, null);
|
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]);
|
operatorList.addOp(OPS.paintInlineImageXObject, [imgData]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -187,7 +189,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
|
|
||||||
|
|
||||||
PDFImage.buildImage(function(imageObj) {
|
PDFImage.buildImage(function(imageObj) {
|
||||||
var imgData = imageObj.createImageData();
|
var imgData = imageObj.createImageData(/* forceRGBA = */ false);
|
||||||
self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData],
|
self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData],
|
||||||
null, [imgData.data.buffer]);
|
null, [imgData.data.buffer]);
|
||||||
}, self.handler, self.xref, resources, image, inline);
|
}, self.handler, self.xref, resources, image, inline);
|
||||||
@ -1313,7 +1315,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
// replacing queue items
|
// replacing queue items
|
||||||
squash(fnArray, j, count * 4, OPS.paintInlineImageXObjectGroup);
|
squash(fnArray, j, count * 4, OPS.paintInlineImageXObjectGroup);
|
||||||
argsArray.splice(j, count * 4,
|
argsArray.splice(j, count * 4,
|
||||||
[{width: imgWidth, height: imgHeight, kind: 'rgba_32bpp',
|
[{width: imgWidth, height: imgHeight, kind: ImageKind.RGBA_32BPP,
|
||||||
data: imgData}, map]);
|
data: imgData}, map]);
|
||||||
i = j;
|
i = j;
|
||||||
ii = argsArray.length;
|
ii = argsArray.length;
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/* globals ColorSpace, error, isArray, isStream, JpegStream, Name, Promise,
|
/* globals ColorSpace, error, isArray, ImageKind, isStream, JpegStream, Name,
|
||||||
Stream, warn, LegacyPromise */
|
Promise, Stream, warn, LegacyPromise */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@ -417,7 +417,7 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
buffer[i + 2] = clamp((buffer[i + 2] - matteRgb[2]) * k + matteRgb[2]);
|
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 drawWidth = this.drawWidth;
|
||||||
var drawHeight = this.drawHeight;
|
var drawHeight = this.drawHeight;
|
||||||
var imgData = { // other fields are filled in below
|
var imgData = { // other fields are filled in below
|
||||||
@ -430,32 +430,41 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
var originalHeight = this.height;
|
var originalHeight = this.height;
|
||||||
var bpc = this.bpc;
|
var bpc = this.bpc;
|
||||||
|
|
||||||
// rows start at byte boundary;
|
// Rows start at byte boundary.
|
||||||
var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
|
var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
|
||||||
var imgArray = this.getImageBytes(originalHeight * rowBytes);
|
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 = ImageKind.GRAYSCALE_1BPP;
|
||||||
|
} else if (this.colorSpace.name === 'DeviceRGB' && bpc === 8) {
|
||||||
|
kind = ImageKind.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;
|
||||||
|
return imgData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// imgArray can be incomplete (e.g. after CCITT fax encoding).
|
||||||
var actualHeight = 0 | (imgArray.length / rowBytes *
|
var actualHeight = 0 | (imgArray.length / rowBytes *
|
||||||
drawHeight / originalHeight);
|
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 comps = this.getComponents(imgArray);
|
||||||
|
|
||||||
var rgbaBuf = new Uint8Array(drawWidth * drawHeight * 4);
|
var rgbaBuf = new Uint8Array(drawWidth * drawHeight * 4);
|
||||||
@ -473,7 +482,7 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
|
|
||||||
this.undoPreblend(rgbaBuf, drawWidth, actualHeight);
|
this.undoPreblend(rgbaBuf, drawWidth, actualHeight);
|
||||||
|
|
||||||
imgData.kind = 'rgba_32bpp';
|
imgData.kind = ImageKind.RGBA_32BPP;
|
||||||
imgData.data = rgbaBuf;
|
imgData.data = rgbaBuf;
|
||||||
return imgData;
|
return imgData;
|
||||||
},
|
},
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/* globals ColorSpace, DeviceCmykCS, DeviceGrayCS, DeviceRgbCS, error,
|
/* globals ColorSpace, DeviceCmykCS, DeviceGrayCS, DeviceRgbCS, error,
|
||||||
FONT_IDENTITY_MATRIX, IDENTITY_MATRIX, ImageData, isArray, isNum,
|
FONT_IDENTITY_MATRIX, IDENTITY_MATRIX, ImageData, ImageKind,
|
||||||
TilingPattern, OPS, Promise, Util, warn, assert, info, shadow,
|
isArray, isNum, TilingPattern, OPS, Promise, Util, warn, assert,
|
||||||
TextRenderingMode, getShadingPatternFromIR */
|
info, shadow, TextRenderingMode, getShadingPatternFromIR */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@ -452,19 +452,17 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
var chunkImgData = ctx.createImageData(width, fullChunkHeight);
|
var chunkImgData = ctx.createImageData(width, fullChunkHeight);
|
||||||
var srcPos = 0;
|
var srcPos = 0;
|
||||||
var src = imgData.data;
|
var src = imgData.data;
|
||||||
var dst = chunkImgData.data;
|
var dest = chunkImgData.data;
|
||||||
|
|
||||||
// There are multiple forms in which the pixel data can be passed, and
|
// There are multiple forms in which the pixel data can be passed, and
|
||||||
// imgData.kind tells us which one this is.
|
// imgData.kind tells us which one this is.
|
||||||
|
|
||||||
if (imgData.kind === 'grayscale_1bpp') {
|
if (imgData.kind === ImageKind.GRAYSCALE_1BPP) {
|
||||||
// Grayscale, 1 bit per pixel (i.e. black-and-white).
|
// Grayscale, 1 bit per pixel (i.e. black-and-white).
|
||||||
var srcData = imgData.data;
|
var destDataLength = dest.length;
|
||||||
var destData = chunkImgData.data;
|
var srcLength = src.byteLength;
|
||||||
var destDataLength = destData.length;
|
|
||||||
var origLength = imgData.origLength;
|
|
||||||
for (var i = 3; i < destDataLength; i += 4) {
|
for (var i = 3; i < destDataLength; i += 4) {
|
||||||
destData[i] = 255;
|
dest[i] = 255;
|
||||||
}
|
}
|
||||||
for (var i = 0; i < totalChunks; i++) {
|
for (var i = 0; i < totalChunks; i++) {
|
||||||
var thisChunkHeight =
|
var thisChunkHeight =
|
||||||
@ -475,21 +473,21 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
var srcByte = 0;
|
var srcByte = 0;
|
||||||
for (var k = 0; k < width; k++, destPos += 4) {
|
for (var k = 0; k < width; k++, destPos += 4) {
|
||||||
if (mask === 0) {
|
if (mask === 0) {
|
||||||
if (srcPos >= origLength) {
|
if (srcPos >= srcLength) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
srcByte = srcData[srcPos++];
|
srcByte = src[srcPos++];
|
||||||
mask = 128;
|
mask = 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((srcByte & mask)) {
|
if ((srcByte & mask)) {
|
||||||
destData[destPos] = 255;
|
dest[destPos] = 255;
|
||||||
destData[destPos + 1] = 255;
|
dest[destPos + 1] = 255;
|
||||||
destData[destPos + 2] = 255;
|
dest[destPos + 2] = 255;
|
||||||
} else {
|
} else {
|
||||||
destData[destPos] = 0;
|
dest[destPos] = 0;
|
||||||
destData[destPos + 1] = 0;
|
dest[destPos + 1] = 0;
|
||||||
destData[destPos + 2] = 0;
|
dest[destPos + 2] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mask >>= 1;
|
mask >>= 1;
|
||||||
@ -499,7 +497,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
// We ran out of input. Make all remaining pixels transparent.
|
// We ran out of input. Make all remaining pixels transparent.
|
||||||
destPos += 3;
|
destPos += 3;
|
||||||
do {
|
do {
|
||||||
destData[destPos] = 0;
|
dest[destPos] = 0;
|
||||||
destPos += 4;
|
destPos += 4;
|
||||||
} while (destPos < destDataLength);
|
} while (destPos < destDataLength);
|
||||||
}
|
}
|
||||||
@ -507,25 +505,41 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
|
ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (imgData.kind === 'rgba_32bpp') {
|
} else if (imgData.kind === ImageKind.RGBA_32BPP) {
|
||||||
// RGBA, 32-bits per pixel.
|
// RGBA, 32-bits per pixel.
|
||||||
var haveSetAndSubarray = 'set' in dst && 'subarray' in src;
|
var haveSetAndSubarray = 'set' in dest && 'subarray' in src;
|
||||||
|
|
||||||
for (var i = 0; i < totalChunks; i++) {
|
for (var i = 0; i < totalChunks; i++) {
|
||||||
var thisChunkHeight =
|
var thisChunkHeight =
|
||||||
(i < fullChunks) ? fullChunkHeight : partialChunkHeight;
|
(i < fullChunks) ? fullChunkHeight : partialChunkHeight;
|
||||||
var elemsInThisChunk = imgData.width * thisChunkHeight * 4;
|
var elemsInThisChunk = imgData.width * thisChunkHeight * 4;
|
||||||
if (haveSetAndSubarray) {
|
if (haveSetAndSubarray) {
|
||||||
dst.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
|
dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
|
||||||
srcPos += elemsInThisChunk;
|
srcPos += elemsInThisChunk;
|
||||||
} else {
|
} else {
|
||||||
for (var j = 0; j < elemsInThisChunk; j++) {
|
for (var j = 0; j < elemsInThisChunk; j++) {
|
||||||
chunkImgData.data[j] = imgData.data[srcPos++];
|
dest[j] = src[srcPos++];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
|
ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (imgData.kind === ImageKind.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) {
|
||||||
|
dest[destPos++] = src[srcPos++];
|
||||||
|
dest[destPos++] = src[srcPos++];
|
||||||
|
dest[destPos++] = src[srcPos++];
|
||||||
|
dest[destPos++] = 255;
|
||||||
|
}
|
||||||
|
ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
error('bad image kind: ' + imgData.kind);
|
error('bad image kind: ' + imgData.kind);
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,12 @@ var TextRenderingMode = {
|
|||||||
ADD_TO_PATH_FLAG: 4
|
ADD_TO_PATH_FLAG: 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var ImageKind = {
|
||||||
|
GRAYSCALE_1BPP: 1,
|
||||||
|
RGB_24BPP: 2,
|
||||||
|
RGBA_32BPP: 3
|
||||||
|
};
|
||||||
|
|
||||||
// The global PDFJS object exposes the API
|
// The global PDFJS object exposes the API
|
||||||
// In production, it will be declared outside a global wrapper
|
// In production, it will be declared outside a global wrapper
|
||||||
// In development, it will be declared here
|
// In development, it will be declared here
|
||||||
|
Loading…
x
Reference in New Issue
Block a user