diff --git a/src/core/evaluator.js b/src/core/evaluator.js index fc02dc58b9ea4..660630ce99dfa 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -1352,6 +1352,7 @@ class PartialEvaluator { if (!args) { args = []; } + let minMax; if ( lastIndex < 0 || operatorList.fnArray[lastIndex] !== OPS.constructPath @@ -1368,7 +1369,8 @@ class PartialEvaluator { operatorList.addOp(OPS.save, null); } - operatorList.addOp(OPS.constructPath, [[fn], args]); + minMax = [Infinity, -Infinity, Infinity, -Infinity]; + operatorList.addOp(OPS.constructPath, [[fn], args, minMax]); if (parsingText) { operatorList.addOp(OPS.restore, null); @@ -1377,6 +1379,28 @@ class PartialEvaluator { const opArgs = operatorList.argsArray[lastIndex]; opArgs[0].push(fn); Array.prototype.push.apply(opArgs[1], args); + minMax = opArgs[2]; + } + + // Compute min/max in the worker instead of the main thread. + // If the current matrix (when drawing) is a scaling one + // then min/max can be easily computed in using those values. + // Only rectangle, lineTo and moveTo are handled here since + // Bezier stuff requires to have the starting point. + switch (fn) { + case OPS.rectangle: + minMax[0] = Math.min(minMax[0], args[0], args[0] + args[2]); + minMax[1] = Math.max(minMax[1], args[0], args[0] + args[2]); + minMax[2] = Math.min(minMax[2], args[1], args[1] + args[3]); + minMax[3] = Math.max(minMax[3], args[1], args[1] + args[3]); + break; + case OPS.moveTo: + case OPS.lineTo: + minMax[0] = Math.min(minMax[0], args[0]); + minMax[1] = Math.max(minMax[1], args[0]); + minMax[2] = Math.min(minMax[2], args[1]); + minMax[3] = Math.max(minMax[3], args[1]); + break; } } diff --git a/src/display/canvas.js b/src/display/canvas.js index c1c102ba1e93b..769fd802a2d22 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -604,8 +604,23 @@ class CanvasExtraState { this.maxY = Math.max(this.maxY, y); } - updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3) { + updateScalingPathMinMax(transform, minMax) { + Util.scaleMinMax(transform, minMax); + this.minX = Math.min(this.minX, minMax[0]); + this.maxX = Math.max(this.maxX, minMax[1]); + this.minY = Math.min(this.minY, minMax[2]); + this.maxY = Math.max(this.maxY, minMax[3]); + } + + updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) { const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3); + if (minMax) { + minMax[0] = Math.min(minMax[0], box[0], box[2]); + minMax[1] = Math.max(minMax[1], box[0], box[2]); + minMax[2] = Math.min(minMax[2], box[1], box[3]); + minMax[3] = Math.max(minMax[3], box[1], box[3]); + return; + } this.updatePathMinMax(transform, box[0], box[1]); this.updatePathMinMax(transform, box[2], box[3]); } @@ -1737,12 +1752,25 @@ class CanvasGraphics { } // Path - constructPath(ops, args) { + constructPath(ops, args, minMax) { const ctx = this.ctx; const current = this.current; let x = current.x, y = current.y; let startX, startY; + const currentTransform = ctx.mozCurrentTransform; + + // Most of the time the current transform is a scaling matrix + // so we don't need to transform points before computing min/max: + // we can compute min/max first and then smartly "apply" the + // transform (see Util.scaleMinMax). + // For rectangle, moveTo and lineTo, min/max are computed in the + // worker (see evaluator.js). + const isScalingMatrix = + (currentTransform[0] === 0 && currentTransform[3] === 0) || + (currentTransform[1] === 0 && currentTransform[2] === 0); + const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null; + for (let i = 0, j = 0, ii = ops.length; i < ii; i++) { switch (ops[i] | 0) { case OPS.rectangle: @@ -1761,21 +1789,27 @@ class CanvasGraphics { ctx.lineTo(xw, yh); ctx.lineTo(x, yh); } - current.updatePathMinMax(ctx.mozCurrentTransform, x, y); - current.updatePathMinMax(ctx.mozCurrentTransform, xw, yh); + if (!isScalingMatrix) { + current.updatePathMinMax(currentTransform, x, y); + current.updatePathMinMax(currentTransform, xw, yh); + } ctx.closePath(); break; case OPS.moveTo: x = args[j++]; y = args[j++]; ctx.moveTo(x, y); - current.updatePathMinMax(ctx.mozCurrentTransform, x, y); + if (!isScalingMatrix) { + current.updatePathMinMax(currentTransform, x, y); + } break; case OPS.lineTo: x = args[j++]; y = args[j++]; ctx.lineTo(x, y); - current.updatePathMinMax(ctx.mozCurrentTransform, x, y); + if (!isScalingMatrix) { + current.updatePathMinMax(currentTransform, x, y); + } break; case OPS.curveTo: startX = x; @@ -1791,7 +1825,7 @@ class CanvasGraphics { y ); current.updateCurvePathMinMax( - ctx.mozCurrentTransform, + currentTransform, startX, startY, args[j], @@ -1799,7 +1833,8 @@ class CanvasGraphics { args[j + 2], args[j + 3], x, - y + y, + minMaxForBezier ); j += 6; break; @@ -1815,7 +1850,7 @@ class CanvasGraphics { args[j + 3] ); current.updateCurvePathMinMax( - ctx.mozCurrentTransform, + currentTransform, startX, startY, x, @@ -1823,7 +1858,8 @@ class CanvasGraphics { args[j], args[j + 1], args[j + 2], - args[j + 3] + args[j + 3], + minMaxForBezier ); x = args[j + 2]; y = args[j + 3]; @@ -1836,7 +1872,7 @@ class CanvasGraphics { y = args[j + 3]; ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y); current.updateCurvePathMinMax( - ctx.mozCurrentTransform, + currentTransform, startX, startY, args[j], @@ -1844,7 +1880,8 @@ class CanvasGraphics { x, y, x, - y + y, + minMaxForBezier ); j += 4; break; @@ -1853,6 +1890,11 @@ class CanvasGraphics { break; } } + + if (isScalingMatrix) { + current.updateScalingPathMinMax(currentTransform, minMaxForBezier); + } + current.setCurrentPoint(x, y); } diff --git a/src/shared/util.js b/src/shared/util.js index aa5c01ff79f65..5e5529e4150e0 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -720,6 +720,57 @@ class Util { return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`; } + // Apply a scaling matrix to some min/max values. + // If a scaling factor is negative then min and max must be + // swaped. + static scaleMinMax(transform, minMax) { + let temp; + if (transform[0]) { + if (transform[0] < 0) { + temp = minMax[0]; + minMax[0] = minMax[1]; + minMax[1] = temp; + } + minMax[0] *= transform[0]; + minMax[1] *= transform[0]; + + if (transform[3] < 0) { + temp = minMax[2]; + minMax[2] = minMax[3]; + minMax[3] = temp; + } + minMax[2] *= transform[3]; + minMax[3] *= transform[3]; + } else { + temp = minMax[0]; + minMax[0] = minMax[2]; + minMax[2] = temp; + temp = minMax[1]; + minMax[1] = minMax[3]; + minMax[3] = temp; + + if (transform[1] < 0) { + temp = minMax[2]; + minMax[2] = minMax[3]; + minMax[3] = temp; + } + minMax[2] *= transform[1]; + minMax[3] *= transform[1]; + + if (transform[2] < 0) { + temp = minMax[0]; + minMax[0] = minMax[1]; + minMax[1] = temp; + } + minMax[0] *= transform[2]; + minMax[1] *= transform[2]; + } + minMax[0] += transform[4]; + minMax[1] += transform[4]; + minMax[2] += transform[5]; + minMax[3] += transform[5]; + } + // Concatenates two transformation matrices together and returns the result. static transform(m1, m2) { return [