Skip to content

Commit 4b7691b

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 4b7691b

File tree

3 files changed

+130
-13
lines changed

3 files changed

+130
-13
lines changed

src/core/evaluator.js

+25-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,28 @@ 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 be easily 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]);
1403+
break;
13801404
}
13811405
}
13821406

src/display/canvas.js

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

607-
updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3) {
607+
updateScalingPathMinMax(transform, minMax) {
608+
Util.scaleMinMax(transform, minMax);
609+
this.minX = Math.min(this.minX, minMax[0]);
610+
this.maxX = Math.max(this.maxX, minMax[1]);
611+
this.minY = Math.min(this.minY, minMax[2]);
612+
this.maxY = Math.max(this.maxY, minMax[3]);
613+
}
614+
615+
updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
608616
const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3);
617+
if (minMax) {
618+
minMax[0] = Math.min(minMax[0], box[0], box[2]);
619+
minMax[1] = Math.max(minMax[1], box[0], box[2]);
620+
minMax[2] = Math.min(minMax[2], box[1], box[3]);
621+
minMax[3] = Math.max(minMax[3], box[1], box[3]);
622+
return;
623+
}
609624
this.updatePathMinMax(transform, box[0], box[1]);
610625
this.updatePathMinMax(transform, box[2], box[3]);
611626
}
@@ -1737,12 +1752,25 @@ class CanvasGraphics {
17371752
}
17381753

17391754
// Path
1740-
constructPath(ops, args) {
1755+
constructPath(ops, args, minMax) {
17411756
const ctx = this.ctx;
17421757
const current = this.current;
17431758
let x = current.x,
17441759
y = current.y;
17451760
let startX, startY;
1761+
const currentTransform = ctx.mozCurrentTransform;
1762+
1763+
// Most of the time the current transform is a scaling matrix
1764+
// so we don't need to transform points before computing min/max:
1765+
// we can compute min/max first and then smartly "apply" the
1766+
// transform (see Util.scaleMinMax).
1767+
// For rectangle, moveTo and lineTo, min/max are computed in the
1768+
// worker (see evaluator.js).
1769+
const isScalingMatrix =
1770+
(currentTransform[0] === 0 && currentTransform[3] === 0) ||
1771+
(currentTransform[1] === 0 && currentTransform[2] === 0);
1772+
const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null;
1773+
17461774
for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
17471775
switch (ops[i] | 0) {
17481776
case OPS.rectangle:
@@ -1761,21 +1789,27 @@ class CanvasGraphics {
17611789
ctx.lineTo(xw, yh);
17621790
ctx.lineTo(x, yh);
17631791
}
1764-
current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
1765-
current.updatePathMinMax(ctx.mozCurrentTransform, xw, yh);
1792+
if (!isScalingMatrix) {
1793+
current.updatePathMinMax(currentTransform, x, y);
1794+
current.updatePathMinMax(currentTransform, xw, yh);
1795+
}
17661796
ctx.closePath();
17671797
break;
17681798
case OPS.moveTo:
17691799
x = args[j++];
17701800
y = args[j++];
17711801
ctx.moveTo(x, y);
1772-
current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
1802+
if (!isScalingMatrix) {
1803+
current.updatePathMinMax(currentTransform, x, y);
1804+
}
17731805
break;
17741806
case OPS.lineTo:
17751807
x = args[j++];
17761808
y = args[j++];
17771809
ctx.lineTo(x, y);
1778-
current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
1810+
if (!isScalingMatrix) {
1811+
current.updatePathMinMax(currentTransform, x, y);
1812+
}
17791813
break;
17801814
case OPS.curveTo:
17811815
startX = x;
@@ -1791,15 +1825,16 @@ class CanvasGraphics {
17911825
y
17921826
);
17931827
current.updateCurvePathMinMax(
1794-
ctx.mozCurrentTransform,
1828+
currentTransform,
17951829
startX,
17961830
startY,
17971831
args[j],
17981832
args[j + 1],
17991833
args[j + 2],
18001834
args[j + 3],
18011835
x,
1802-
y
1836+
y,
1837+
minMaxForBezier
18031838
);
18041839
j += 6;
18051840
break;
@@ -1815,15 +1850,16 @@ class CanvasGraphics {
18151850
args[j + 3]
18161851
);
18171852
current.updateCurvePathMinMax(
1818-
ctx.mozCurrentTransform,
1853+
currentTransform,
18191854
startX,
18201855
startY,
18211856
x,
18221857
y,
18231858
args[j],
18241859
args[j + 1],
18251860
args[j + 2],
1826-
args[j + 3]
1861+
args[j + 3],
1862+
minMaxForBezier
18271863
);
18281864
x = args[j + 2];
18291865
y = args[j + 3];
@@ -1836,15 +1872,16 @@ class CanvasGraphics {
18361872
y = args[j + 3];
18371873
ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
18381874
current.updateCurvePathMinMax(
1839-
ctx.mozCurrentTransform,
1875+
currentTransform,
18401876
startX,
18411877
startY,
18421878
args[j],
18431879
args[j + 1],
18441880
x,
18451881
y,
18461882
x,
1847-
y
1883+
y,
1884+
minMaxForBezier
18481885
);
18491886
j += 4;
18501887
break;
@@ -1853,6 +1890,11 @@ class CanvasGraphics {
18531890
break;
18541891
}
18551892
}
1893+
1894+
if (isScalingMatrix) {
1895+
current.updateScalingPathMinMax(currentTransform, minMaxForBezier);
1896+
}
1897+
18561898
current.setCurrentPoint(x, y);
18571899
}
18581900

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+
// Apply a scaling matrix to some min/max values.
724+
// If a scaling factor is negative then min and max must be
725+
// swaped.
726+
static scaleMinMax(transform, minMax) {
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)