diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 9cf4a4463..b52a52da2 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -262,8 +262,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var inverseDecode = !!decode && decode[0] > 0; operatorList.addOp(OPS.paintImageMaskXObject, - [PDFImage.createMask(imgArray, width, height, - inverseDecode)] + [PDFImage.createMask(imgArray, width, height, inverseDecode)] ); return; } diff --git a/src/core/image.js b/src/core/image.js index e7baf4fb2..f4781c03f 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -209,26 +209,21 @@ var PDFImage = (function PDFImageClosure() { PDFImage.createMask = function PDFImage_createMask(imgArray, width, height, inverseDecode) { - var buffer = new Uint8Array(width * height * 4); - var imgArrayPos = 0; - var i, j, mask, buf; - // removing making non-masked pixels transparent - var bufferPos = 3; // alpha component offset - for (i = 0; i < height; i++) { - mask = 0; - for (j = 0; j < width; j++) { - if (!mask) { - buf = imgArray[imgArrayPos++]; - mask = 128; - } - if (!(buf & mask) !== inverseDecode) { - buffer[bufferPos] = 255; - } - bufferPos += 4; - mask >>= 1; + // Copy imgArray into a typed array (inverting if necessary) so it can be + // transferred to the main thread. + var length = ((width + 7) >> 3) * height; + var data = new Uint8Array(length); + if (inverseDecode) { + for (var i = 0; i < length; i++) { + data[i] = ~imgArray[i]; + } + } else { + for (var i = 0; i < length; i++) { + data[i] = imgArray[i]; } } - return {data: buffer, width: width, height: height}; + + return {data: data, width: width, height: height}; }; PDFImage.prototype = { diff --git a/src/display/canvas.js b/src/display/canvas.js index f4f04e611..40a2bc1df 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -191,6 +191,18 @@ function compileType3Glyph(imgData) { var points = new Uint8Array(width1 * (height + 1)); var POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]); + + // decodes bit-packed mask data + var lineSize = (width + 7) & ~7, data0 = imgData.data; + var data = new Uint8Array(lineSize * height), pos = 0, ii; + for (i = 0, ii = data0.length; i < ii; i++) { + var mask = 128, elem = data0[i]; + while (mask > 0) { + data[pos++] = (elem & mask) ? 0 : 255; + mask >>= 1; + } + } + // finding iteresting points: every point is located between mask pixels, // so there will be points of the (width + 1)x(height + 1) grid. Every point // will have flags assigned based on neighboring mask pixels: @@ -201,24 +213,25 @@ function compileType3Glyph(imgData) { // - outside corners: 1, 2, 4, 8; // - inside corners: 7, 11, 13, 14; // - and, intersections: 5, 10. - var pos = 3, data = imgData.data, lineSize = width * 4, count = 0; - if (data[3] !== 0) { + var count = 0; + pos = 0; + if (data[pos] !== 0) { points[0] = 1; ++count; } for (j = 1; j < width; j++) { - if (data[pos] !== data[pos + 4]) { + if (data[pos] !== data[pos + 1]) { points[j] = data[pos] ? 2 : 1; ++count; } - pos += 4; + pos++; } if (data[pos] !== 0) { points[j] = 2; ++count; } - pos += 4; for (i = 1; i < height; i++) { + pos = i * lineSize; j0 = i * width1; if (data[pos - lineSize] !== data[pos]) { points[j0] = data[pos] ? 1 : 8; @@ -228,37 +241,36 @@ function compileType3Glyph(imgData) { // array (in order 8-1-2-4, so we can use '>>2' to shift the column). var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0); for (j = 1; j < width; j++) { - sum = (sum >> 2) + (data[pos + 4] ? 4 : 0) + - (data[pos - lineSize + 4] ? 8 : 0); + sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + + (data[pos - lineSize + 1] ? 8 : 0); if (POINT_TYPES[sum]) { points[j0 + j] = POINT_TYPES[sum]; ++count; } - pos += 4; + pos++; } if (data[pos - lineSize] !== data[pos]) { points[j0 + j] = data[pos] ? 2 : 4; ++count; } - pos += 4; if (count > POINT_TO_PROCESS_LIMIT) { return null; } } - pos -= lineSize; + pos = lineSize * (height - 1); j0 = i * width1; if (data[pos] !== 0) { points[j0] = 8; ++count; } for (j = 1; j < width; j++) { - if (data[pos] !== data[pos + 4]) { + if (data[pos] !== data[pos + 1]) { points[j0 + j] = data[pos] ? 4 : 8; ++count; } - pos += 4; + pos++; } if (data[pos] !== 0) { points[j0 + j] = 4; @@ -431,6 +443,34 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { ctx.putImageData(tmpImgData, 0, 0); } + function putBinaryImageMask(ctx, imgData) { + var width = imgData.width, height = imgData.height; + var tmpImgData = ctx.createImageData(width, height); + var data = imgData.data; + var tmpImgDataPixels = tmpImgData.data; + var dataPos = 0; + + // Expand the mask so it can be used by the canvas. Any required inversion + // has already been handled. + var tmpPos = 3; // alpha component offset + for (var i = 0; i < height; i++) { + var mask = 0; + for (var j = 0; j < width; j++) { + if (!mask) { + var elem = data[dataPos++]; + mask = 128; + } + if (!(elem & mask)) { + tmpImgDataPixels[tmpPos] = 255; + } + tmpPos += 4; + mask >>= 1; + } + } + + ctx.putImageData(tmpImgData, 0, 0); + } + function copyCtxState(sourceCtx, destCtx) { var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha', 'lineWidth', 'lineCap', 'lineJoin', 'miterLimit', @@ -1611,7 +1651,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var maskCtx = maskCanvas.context; maskCtx.save(); - putBinaryImageData(maskCtx, img); + putBinaryImageMask(maskCtx, img); maskCtx.globalCompositeOperation = 'source-in'; @@ -1638,7 +1678,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var maskCtx = maskCanvas.context; maskCtx.save(); - putBinaryImageData(maskCtx, image); + putBinaryImageMask(maskCtx, image); maskCtx.globalCompositeOperation = 'source-in';