Skip to content

Commit

Permalink
Merge pull request #14796 from calixteman/minmax
Browse files Browse the repository at this point in the history
[api-minor] Simplify min/max computations in constructPath (bug 1135277)
  • Loading branch information
Snuffleupagus authored Apr 18, 2022
2 parents 379125c + 4b7691b commit f828792
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 13 deletions.
26 changes: 25 additions & 1 deletion src/core/evaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,7 @@ class PartialEvaluator {
if (!args) {
args = [];
}
let minMax;
if (
lastIndex < 0 ||
operatorList.fnArray[lastIndex] !== OPS.constructPath
Expand All @@ -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);
Expand All @@ -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;
}
}

Expand Down
66 changes: 54 additions & 12 deletions src/display/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}
Expand Down Expand Up @@ -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:
Expand All @@ -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;
Expand All @@ -1791,15 +1825,16 @@ class CanvasGraphics {
y
);
current.updateCurvePathMinMax(
ctx.mozCurrentTransform,
currentTransform,
startX,
startY,
args[j],
args[j + 1],
args[j + 2],
args[j + 3],
x,
y
y,
minMaxForBezier
);
j += 6;
break;
Expand All @@ -1815,15 +1850,16 @@ class CanvasGraphics {
args[j + 3]
);
current.updateCurvePathMinMax(
ctx.mozCurrentTransform,
currentTransform,
startX,
startY,
x,
y,
args[j],
args[j + 1],
args[j + 2],
args[j + 3]
args[j + 3],
minMaxForBezier
);
x = args[j + 2];
y = args[j + 3];
Expand All @@ -1836,15 +1872,16 @@ 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],
args[j + 1],
x,
y,
x,
y
y,
minMaxForBezier
);
j += 4;
break;
Expand All @@ -1853,6 +1890,11 @@ class CanvasGraphics {
break;
}
}

if (isScalingMatrix) {
current.updateScalingPathMinMax(currentTransform, minMaxForBezier);
}

current.setCurrentPoint(x, y);
}

Expand Down
51 changes: 51 additions & 0 deletions src/shared/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 [
Expand Down

0 comments on commit f828792

Please sign in to comment.