Skip to content

Commit 2022716

Browse files
committed
Simplify min/max computations in constructPath (bug 1135277)
- most of the time the current transform is a scaling one (modulo translation), hence it's possible to avoid to apply the transform on each bbox and then apply it a posteriori; - compute the bbox when it's possible in the worker.
1 parent 379125c commit 2022716

File tree

3 files changed

+125
-13
lines changed

3 files changed

+125
-13
lines changed

src/core/evaluator.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,7 @@ class PartialEvaluator {
13521352
if (!args) {
13531353
args = [];
13541354
}
1355+
let minMax;
13551356
if (
13561357
lastIndex < 0 ||
13571358
operatorList.fnArray[lastIndex] !== OPS.constructPath
@@ -1368,7 +1369,8 @@ class PartialEvaluator {
13681369
operatorList.addOp(OPS.save, null);
13691370
}
13701371

1371-
operatorList.addOp(OPS.constructPath, [[fn], args]);
1372+
minMax = [Infinity, -Infinity, Infinity, -Infinity];
1373+
operatorList.addOp(OPS.constructPath, [[fn], args, minMax]);
13721374

13731375
if (parsingText) {
13741376
operatorList.addOp(OPS.restore, null);
@@ -1377,6 +1379,27 @@ class PartialEvaluator {
13771379
const opArgs = operatorList.argsArray[lastIndex];
13781380
opArgs[0].push(fn);
13791381
Array.prototype.push.apply(opArgs[1], args);
1382+
minMax = opArgs[2];
1383+
}
1384+
1385+
// Compute min/max in the worker instead of the main thread.
1386+
// If the current matrix (when drawing) is a scaling one
1387+
// then min/max can easily be computed in using those values.
1388+
// Only rectangle, lineTo and moveTo are handled here since
1389+
// Bezier stuff requires to have the starting point.
1390+
switch (fn) {
1391+
case OPS.rectangle:
1392+
minMax[0] = Math.min(minMax[0], args[0], args[0] + args[2]);
1393+
minMax[1] = Math.max(minMax[1], args[0], args[0] + args[2]);
1394+
minMax[2] = Math.min(minMax[2], args[1], args[1] + args[3]);
1395+
minMax[3] = Math.max(minMax[3], args[1], args[1] + args[3]);
1396+
break;
1397+
case OPS.moveTo:
1398+
case OPS.lineTo:
1399+
minMax[0] = Math.min(minMax[0], args[0]);
1400+
minMax[1] = Math.max(minMax[1], args[0]);
1401+
minMax[2] = Math.min(minMax[2], args[1]);
1402+
minMax[3] = Math.max(minMax[3], args[1]);
13801403
}
13811404
}
13821405

src/display/canvas.js

+50-12
Original file line numberDiff line numberDiff line change
@@ -604,8 +604,15 @@ class CanvasExtraState {
604604
this.maxY = Math.max(this.maxY, y);
605605
}
606606

607-
updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3) {
607+
updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
608608
const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3);
609+
if (minMax) {
610+
minMax[0] = Math.min(minMax[0], box[0], box[2]);
611+
minMax[1] = Math.min(minMax[1], box[0], box[2]);
612+
minMax[2] = Math.min(minMax[2], box[1], box[3]);
613+
minMax[3] = Math.min(minMax[3], box[1], box[3]);
614+
return;
615+
}
609616
this.updatePathMinMax(transform, box[0], box[1]);
610617
this.updatePathMinMax(transform, box[2], box[3]);
611618
}
@@ -1737,12 +1744,25 @@ class CanvasGraphics {
17371744
}
17381745

17391746
// Path
1740-
constructPath(ops, args) {
1747+
constructPath(ops, args, minMax) {
17411748
const ctx = this.ctx;
17421749
const current = this.current;
17431750
let x = current.x,
17441751
y = current.y;
17451752
let startX, startY;
1753+
const currentTransform = ctx.mozCurrentTransform;
1754+
1755+
// Most of the time the current transform is a scaling matrix
1756+
// so we don't need to transform points before computing min/max:
1757+
// we can compute min/max first and then smartly "apply" the
1758+
// transform (see Util.scaleMinMax).
1759+
// For rectangle, moveTo and lineTo, min/max are computed in the
1760+
// worker (see evaluator.js).
1761+
const isScalingMatrix =
1762+
(currentTransform[0] === 0 && currentTransform[3] === 0) ||
1763+
(currentTransform[1] === 0 && currentTransform[2] === 0);
1764+
const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null;
1765+
17461766
for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
17471767
switch (ops[i] | 0) {
17481768
case OPS.rectangle:
@@ -1761,21 +1781,27 @@ class CanvasGraphics {
17611781
ctx.lineTo(xw, yh);
17621782
ctx.lineTo(x, yh);
17631783
}
1764-
current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
1765-
current.updatePathMinMax(ctx.mozCurrentTransform, xw, yh);
1784+
if (!isScalingMatrix) {
1785+
current.updatePathMinMax(currentTransform, x, y);
1786+
current.updatePathMinMax(currentTransform, xw, yh);
1787+
}
17661788
ctx.closePath();
17671789
break;
17681790
case OPS.moveTo:
17691791
x = args[j++];
17701792
y = args[j++];
17711793
ctx.moveTo(x, y);
1772-
current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
1794+
if (!isScalingMatrix) {
1795+
current.updatePathMinMax(currentTransform, x, y);
1796+
}
17731797
break;
17741798
case OPS.lineTo:
17751799
x = args[j++];
17761800
y = args[j++];
17771801
ctx.lineTo(x, y);
1778-
current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
1802+
if (!isScalingMatrix) {
1803+
current.updatePathMinMax(currentTransform, x, y);
1804+
}
17791805
break;
17801806
case OPS.curveTo:
17811807
startX = x;
@@ -1791,15 +1817,16 @@ class CanvasGraphics {
17911817
y
17921818
);
17931819
current.updateCurvePathMinMax(
1794-
ctx.mozCurrentTransform,
1820+
currentTransform,
17951821
startX,
17961822
startY,
17971823
args[j],
17981824
args[j + 1],
17991825
args[j + 2],
18001826
args[j + 3],
18011827
x,
1802-
y
1828+
y,
1829+
minMaxForBezier
18031830
);
18041831
j += 6;
18051832
break;
@@ -1815,15 +1842,16 @@ class CanvasGraphics {
18151842
args[j + 3]
18161843
);
18171844
current.updateCurvePathMinMax(
1818-
ctx.mozCurrentTransform,
1845+
currentTransform,
18191846
startX,
18201847
startY,
18211848
x,
18221849
y,
18231850
args[j],
18241851
args[j + 1],
18251852
args[j + 2],
1826-
args[j + 3]
1853+
args[j + 3],
1854+
minMaxForBezier
18271855
);
18281856
x = args[j + 2];
18291857
y = args[j + 3];
@@ -1836,15 +1864,16 @@ class CanvasGraphics {
18361864
y = args[j + 3];
18371865
ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
18381866
current.updateCurvePathMinMax(
1839-
ctx.mozCurrentTransform,
1867+
currentTransform,
18401868
startX,
18411869
startY,
18421870
args[j],
18431871
args[j + 1],
18441872
x,
18451873
y,
18461874
x,
1847-
y
1875+
y,
1876+
minMaxForBezier
18481877
);
18491878
j += 4;
18501879
break;
@@ -1853,6 +1882,15 @@ class CanvasGraphics {
18531882
break;
18541883
}
18551884
}
1885+
1886+
if (isScalingMatrix) {
1887+
Util.scaleMinMax(currentTransform, minMaxForBezier);
1888+
current.minX = Math.min(current.minX, minMaxForBezier[0]);
1889+
current.maxX = Math.max(current.maxX, minMaxForBezier[1]);
1890+
current.minY = Math.min(current.minY, minMaxForBezier[2]);
1891+
current.maxY = Math.max(current.maxY, [3]);
1892+
}
1893+
18561894
current.setCurrentPoint(x, y);
18571895
}
18581896

src/shared/util.js

+51
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,57 @@ class Util {
720720
return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`;
721721
}
722722

723+
static scaleMinMax(transform, minMax) {
724+
// Apply a scaling matrix to some min/max values.
725+
// If a scaling factor is negative then min and max must be
726+
// swaped.
727+
let temp;
728+
if (transform[0]) {
729+
if (transform[0] < 0) {
730+
temp = minMax[0];
731+
minMax[0] = minMax[1];
732+
minMax[1] = temp;
733+
}
734+
minMax[0] *= transform[0];
735+
minMax[1] *= transform[0];
736+
737+
if (transform[3] < 0) {
738+
temp = minMax[2];
739+
minMax[2] = minMax[3];
740+
minMax[3] = temp;
741+
}
742+
minMax[2] *= transform[3];
743+
minMax[3] *= transform[3];
744+
} else {
745+
temp = minMax[0];
746+
minMax[0] = minMax[2];
747+
minMax[2] = temp;
748+
temp = minMax[1];
749+
minMax[1] = minMax[3];
750+
minMax[3] = temp;
751+
752+
if (transform[1] < 0) {
753+
temp = minMax[2];
754+
minMax[2] = minMax[3];
755+
minMax[3] = temp;
756+
}
757+
minMax[2] *= transform[1];
758+
minMax[3] *= transform[1];
759+
760+
if (transform[2] < 0) {
761+
temp = minMax[0];
762+
minMax[0] = minMax[1];
763+
minMax[1] = temp;
764+
}
765+
minMax[0] *= transform[2];
766+
minMax[1] *= transform[2];
767+
}
768+
minMax[0] += transform[4];
769+
minMax[1] += transform[4];
770+
minMax[2] += transform[5];
771+
minMax[3] += transform[5];
772+
}
773+
723774
// Concatenates two transformation matrices together and returns the result.
724775
static transform(m1, m2) {
725776
return [

0 commit comments

Comments
 (0)