diff --git a/src/core/evaluator.js b/src/core/evaluator.js index b06dfe7f1..0cd268cb7 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -1236,136 +1236,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } }; - PartialEvaluator.optimizeQueue = - function PartialEvaluator_optimizeQueue(queue) { - - function squash(array, index, howMany, element) { - if (isArray(array)) { - array.splice(index, howMany, element); - } else { - // Replace the element. - array[index] = element; - // Shift everything after the element up. - var sub = array.subarray(index + howMany); - array.set(sub, index + 1); - } - } - - var fnArray = queue.fnArray, argsArray = queue.argsArray; - // grouping paintInlineImageXObject's into paintInlineImageXObjectGroup - // searching for (save, transform, paintInlineImageXObject, restore)+ - var MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10; - var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200; - var MAX_WIDTH = 1000; - var IMAGE_PADDING = 1; - for (var i = 0, ii = argsArray.length; i < ii; i++) { - if (fnArray[i] === OPS.paintInlineImageXObject && - fnArray[i - 2] === OPS.save && fnArray[i - 1] === OPS.transform && - fnArray[i + 1] === OPS.restore) { - var j = i - 2; - for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) { - } - var count = Math.min((i - j) >> 2, - MAX_IMAGES_IN_INLINE_IMAGES_BLOCK); - if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) { - continue; - } - // assuming that heights of those image is too small (~1 pixel) - // packing as much as possible by lines - var maxX = 0; - var map = [], maxLineHeight = 0; - var currentX = IMAGE_PADDING, currentY = IMAGE_PADDING; - for (var q = 0; q < count; q++) { - var transform = argsArray[j + (q << 2) + 1]; - var img = argsArray[j + (q << 2) + 2][0]; - if (currentX + img.width > MAX_WIDTH) { - // starting new line - maxX = Math.max(maxX, currentX); - currentY += maxLineHeight + 2 * IMAGE_PADDING; - currentX = 0; - maxLineHeight = 0; - } - map.push({ - transform: transform, - x: currentX, y: currentY, - w: img.width, h: img.height - }); - currentX += img.width + 2 * IMAGE_PADDING; - maxLineHeight = Math.max(maxLineHeight, img.height); - } - var imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING; - var imgHeight = currentY + maxLineHeight + IMAGE_PADDING; - var imgData = new Uint8Array(imgWidth * imgHeight * 4); - var imgRowSize = imgWidth << 2; - for (var q = 0; q < count; q++) { - var data = argsArray[j + (q << 2) + 2][0].data; - // copy image by lines and extends pixels into padding - var rowSize = map[q].w << 2; - var dataOffset = 0; - var offset = (map[q].x + map[q].y * imgWidth) << 2; - imgData.set( - data.subarray(0, rowSize), offset - imgRowSize); - for (var k = 0, kk = map[q].h; k < kk; k++) { - imgData.set( - data.subarray(dataOffset, dataOffset + rowSize), offset); - dataOffset += rowSize; - offset += imgRowSize; - } - imgData.set( - data.subarray(dataOffset - rowSize, dataOffset), offset); - while (offset >= 0) { - data[offset - 4] = data[offset]; - data[offset - 3] = data[offset + 1]; - data[offset - 2] = data[offset + 2]; - data[offset - 1] = data[offset + 3]; - data[offset + rowSize] = data[offset + rowSize - 4]; - data[offset + rowSize + 1] = data[offset + rowSize - 3]; - data[offset + rowSize + 2] = data[offset + rowSize - 2]; - data[offset + rowSize + 3] = data[offset + rowSize - 1]; - offset -= imgRowSize; - } - } - // replacing queue items - squash(fnArray, j, count * 4, OPS.paintInlineImageXObjectGroup); - argsArray.splice(j, count * 4, - [{width: imgWidth, height: imgHeight, kind: ImageKind.RGBA_32BPP, - data: imgData}, map]); - i = j; - ii = argsArray.length; - } - } - // grouping paintImageMaskXObject's into paintImageMaskXObjectGroup - // searching for (save, transform, paintImageMaskXObject, restore)+ - var MIN_IMAGES_IN_MASKS_BLOCK = 10; - var MAX_IMAGES_IN_MASKS_BLOCK = 100; - for (var i = 0, ii = argsArray.length; i < ii; i++) { - if (fnArray[i] === OPS.paintImageMaskXObject && - fnArray[i - 2] === OPS.save && fnArray[i - 1] === OPS.transform && - fnArray[i + 1] === OPS.restore) { - var j = i - 2; - for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) { - } - var count = Math.min((i - j) >> 2, - MAX_IMAGES_IN_MASKS_BLOCK); - if (count < MIN_IMAGES_IN_MASKS_BLOCK) { - continue; - } - var images = []; - for (var q = 0; q < count; q++) { - var transform = argsArray[j + (q << 2) + 1]; - var maskParams = argsArray[j + (q << 2) + 2][0]; - images.push({data: maskParams.data, width: maskParams.width, - height: maskParams.height, transform: transform}); - } - // replacing queue items - squash(fnArray, j, count * 4, OPS.paintImageMaskXObjectGroup); - argsArray.splice(j, count * 4, [images]); - i = j; - ii = argsArray.length; - } - } - }; - return PartialEvaluator; })(); @@ -1454,7 +1324,7 @@ var OperatorList = (function OperatorListClosure() { }, flush: function(lastChunk) { - PartialEvaluator.optimizeQueue(this); + new QueueOptimizer().optimize(this); var transfers = getTransfers(this); this.messageHandler.send('RenderPageChunk', { operatorList: { @@ -1758,3 +1628,173 @@ var EvaluatorPreprocessor = (function EvaluatorPreprocessor() { }; return EvaluatorPreprocessor; })(); + +var QueueOptimizer = (function QueueOptimizerClosure() { + function squash(array, index, howMany, element) { + if (isArray(array)) { + array.splice(index, howMany, element); + } else { + // Replace the element. + array[index] = element; + // Shift everything after the element up. + var sub = array.subarray(index + howMany); + array.set(sub, index + 1); + } + } + + function addState(parentState, pattern, fn) { + var state = parentState; + for (var i = 0, ii = pattern.length - 1; i < ii; i++) { + var item = pattern[i]; + state = state[item] || (state[item] = []); + } + state[pattern[pattern.length - 1]] = fn; + } + + var InitialState = []; + + addState(InitialState, + [OPS.save, OPS.transform, OPS.paintInlineImageXObject, OPS.restore], + function foundInlineImageGroup(context) { + // grouping paintInlineImageXObject's into paintInlineImageXObjectGroup + // searching for (save, transform, paintInlineImageXObject, restore)+ + var MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10; + var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200; + var MAX_WIDTH = 1000; + var IMAGE_PADDING = 1; + + var fnArray = context.fnArray, argsArray = context.argsArray; + var j = context.currentOperation - 3, i = j + 4; + var ii = context.operationsLength; + + for (; i < ii && fnArray[i - 4] === fnArray[i]; i++) { + } + var count = Math.min((i - j) >> 2, MAX_IMAGES_IN_INLINE_IMAGES_BLOCK); + if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) { + context.currentOperation = i - 1; + return; + } + // assuming that heights of those image is too small (~1 pixel) + // packing as much as possible by lines + var maxX = 0; + var map = [], maxLineHeight = 0; + var currentX = IMAGE_PADDING, currentY = IMAGE_PADDING; + for (var q = 0; q < count; q++) { + var transform = argsArray[j + (q << 2) + 1]; + var img = argsArray[j + (q << 2) + 2][0]; + if (currentX + img.width > MAX_WIDTH) { + // starting new line + maxX = Math.max(maxX, currentX); + currentY += maxLineHeight + 2 * IMAGE_PADDING; + currentX = 0; + maxLineHeight = 0; + } + map.push({ + transform: transform, + x: currentX, y: currentY, + w: img.width, h: img.height + }); + currentX += img.width + 2 * IMAGE_PADDING; + maxLineHeight = Math.max(maxLineHeight, img.height); + } + var imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING; + var imgHeight = currentY + maxLineHeight + IMAGE_PADDING; + var imgData = new Uint8Array(imgWidth * imgHeight * 4); + var imgRowSize = imgWidth << 2; + for (var q = 0; q < count; q++) { + var data = argsArray[j + (q << 2) + 2][0].data; + // copy image by lines and extends pixels into padding + var rowSize = map[q].w << 2; + var dataOffset = 0; + var offset = (map[q].x + map[q].y * imgWidth) << 2; + imgData.set( + data.subarray(0, rowSize), offset - imgRowSize); + for (var k = 0, kk = map[q].h; k < kk; k++) { + imgData.set( + data.subarray(dataOffset, dataOffset + rowSize), offset); + dataOffset += rowSize; + offset += imgRowSize; + } + imgData.set( + data.subarray(dataOffset - rowSize, dataOffset), offset); + while (offset >= 0) { + data[offset - 4] = data[offset]; + data[offset - 3] = data[offset + 1]; + data[offset - 2] = data[offset + 2]; + data[offset - 1] = data[offset + 3]; + data[offset + rowSize] = data[offset + rowSize - 4]; + data[offset + rowSize + 1] = data[offset + rowSize - 3]; + data[offset + rowSize + 2] = data[offset + rowSize - 2]; + data[offset + rowSize + 3] = data[offset + rowSize - 1]; + offset -= imgRowSize; + } + } + // replacing queue items + squash(fnArray, j, count * 4, OPS.paintInlineImageXObjectGroup); + argsArray.splice(j, count * 4, + [{width: imgWidth, height: imgHeight, kind: ImageKind.RGBA_32BPP, + data: imgData}, map]); + context.currentOperation = j; + context.operationsLength -= count * 4 - 1; + }); + + addState(InitialState, + [OPS.save, OPS.transform, OPS.paintImageMaskXObject, OPS.restore], + function foundImageMaskGroup(context) { + // grouping paintImageMaskXObject's into paintImageMaskXObjectGroup + // searching for (save, transform, paintImageMaskXObject, restore)+ + var MIN_IMAGES_IN_MASKS_BLOCK = 10; + var MAX_IMAGES_IN_MASKS_BLOCK = 100; + + var fnArray = context.fnArray, argsArray = context.argsArray; + var j = context.currentOperation - 3, i = j + 4; + var ii = context.operationsLength; + + for (; i < ii && fnArray[i - 4] === fnArray[i]; i++) { + } + var count = Math.min((i - j) >> 2, MAX_IMAGES_IN_MASKS_BLOCK); + if (count < MIN_IMAGES_IN_MASKS_BLOCK) { + context.currentOperation = i - 1; + return; + } + var images = []; + for (var q = 0; q < count; q++) { + var transform = argsArray[j + (q << 2) + 1]; + var maskParams = argsArray[j + (q << 2) + 2][0]; + images.push({data: maskParams.data, width: maskParams.width, + height: maskParams.height, transform: transform}); + } + // replacing queue items + squash(fnArray, j, count * 4, OPS.paintImageMaskXObjectGroup); + argsArray.splice(j, count * 4, [images]); + + context.currentOperation = j; + context.operationsLength -= count * 4 - 1; + }); + + function QueueOptimizer() { + } + QueueOptimizer.prototype = { + optimize: function QueueOptimizer_optimize(queue) { + var fnArray = queue.fnArray, argsArray = queue.argsArray; + var context = { + currentOperation: 0, + operationsLength: argsArray.length, + fnArray: fnArray, + argsArray: argsArray + }; + var i, ii = argsArray.length; + var state; + for (i = 0; i < ii; i++) { + state = (state || InitialState)[fnArray[i]]; + if (typeof state === 'function') { // we found some handler + context.currentOperation = i; + state = state(context); + i = context.currentOperation; + ii = context.operationsLength; + } + } + } + }; + return QueueOptimizer; +})();