Skip to content

Commit 7eef7df

Browse files
authored
Merge pull request #19763 from calixteman/simplify_updaterect
Replace UpdateRectMinMax by getAxialAlignedBoundingBox
2 parents a6934a4 + e7a9515 commit 7eef7df

File tree

4 files changed

+105
-74
lines changed

4 files changed

+105
-74
lines changed

Diff for: src/core/annotation.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -623,10 +623,9 @@ function getQuadPoints(dict, rect) {
623623

624624
function getTransformMatrix(rect, bbox, matrix) {
625625
// 12.5.5: Algorithm: Appearance streams
626-
const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox(
627-
bbox,
628-
matrix
629-
);
626+
const minMax = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]);
627+
Util.axialAlignedBoundingBox(bbox, matrix, minMax);
628+
const [minX, minY, maxX, maxY] = minMax;
630629
if (minX === maxX || minY === maxY) {
631630
// From real-life file, bbox was [0, 0, 0, 0]. In this case,
632631
// just apply the transform for rect

Diff for: src/display/canvas.js

+40-45
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ const SCALE_MATRIX = new DOMMatrix();
6363
// Used to get some coordinates.
6464
const XY = new Float32Array(2);
6565

66+
// Initial rectangle values for the minMax array.
67+
const MIN_MAX_INIT = new Float32Array([
68+
Infinity,
69+
Infinity,
70+
-Infinity,
71+
-Infinity,
72+
]);
73+
6674
/**
6775
* Overrides certain methods on a 2d ctx so that when they are called they
6876
* will also call the same method on the destCtx. The methods that are
@@ -330,40 +338,19 @@ class CanvasExtraState {
330338
this.activeSMask = null;
331339
this.transferMaps = "none";
332340

333-
this.startNewPathAndClipBox([0, 0, width, height]);
341+
this.clipBox = new Float32Array([0, 0, width, height]);
342+
this.minMax = MIN_MAX_INIT.slice();
334343
}
335344

336345
clone() {
337346
const clone = Object.create(this);
338347
clone.clipBox = this.clipBox.slice();
348+
clone.minMax = this.minMax.slice();
339349
return clone;
340350
}
341351

342-
updateRectMinMax([m0, m1, m2, m3, m4, m5], [r0, r1, r2, r3]) {
343-
const m0r0m4 = m0 * r0 + m4;
344-
const m0r2m4 = m0 * r2 + m4;
345-
const m1r0m5 = m1 * r0 + m5;
346-
const m1r2m5 = m1 * r2 + m5;
347-
const m2r1 = m2 * r1;
348-
const m2r3 = m2 * r3;
349-
const m3r1 = m3 * r1;
350-
const m3r3 = m3 * r3;
351-
const a0 = m0r0m4 + m2r1;
352-
const a1 = m0r2m4 + m2r3;
353-
const a2 = m0r0m4 + m2r3;
354-
const a3 = m0r2m4 + m2r1;
355-
const b0 = m1r0m5 + m3r1;
356-
const b1 = m1r2m5 + m3r3;
357-
const b2 = m1r0m5 + m3r3;
358-
const b3 = m1r2m5 + m3r1;
359-
this.minX = Math.min(this.minX, a0, a1, a2, a3);
360-
this.maxX = Math.max(this.maxX, a0, a1, a2, a3);
361-
this.minY = Math.min(this.minY, b0, b1, b2, b3);
362-
this.maxY = Math.max(this.maxY, b0, b1, b2, b3);
363-
}
364-
365352
getPathBoundingBox(pathType = PathType.FILL, transform = null) {
366-
const box = [this.minX, this.minY, this.maxX, this.maxY];
353+
const box = this.minMax.slice();
367354
if (pathType === PathType.STROKE) {
368355
if (!transform) {
369356
unreachable("Stroke bounding box must include transform.");
@@ -387,15 +374,12 @@ class CanvasExtraState {
387374
}
388375

389376
isEmptyClip() {
390-
return this.minX === Infinity;
377+
return this.minMax[0] === Infinity;
391378
}
392379

393380
startNewPathAndClipBox(box) {
394-
this.clipBox = box;
395-
this.minX = Infinity;
396-
this.minY = Infinity;
397-
this.maxX = 0;
398-
this.maxY = 0;
381+
this.clipBox.set(box, 0);
382+
this.minMax.set(MIN_MAX_INIT, 0);
399383
}
400384

401385
getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) {
@@ -1014,10 +998,9 @@ class CanvasGraphics {
1014998
0,
1015999
]);
10161000
maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
1017-
const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox(
1018-
[0, 0, width, height],
1019-
maskToCanvas
1020-
);
1001+
const minMax = MIN_MAX_INIT.slice();
1002+
Util.axialAlignedBoundingBox([0, 0, width, height], maskToCanvas, minMax);
1003+
const [minX, minY, maxX, maxY] = minMax;
10211004
const drawnWidth = Math.round(maxX - minX) || 1;
10221005
const drawnHeight = Math.round(maxY - minY) || 1;
10231006
const fillCanvas = this.cachedCanvases.getCanvas(
@@ -1458,7 +1441,11 @@ class CanvasGraphics {
14581441
}
14591442
path = path2d;
14601443
}
1461-
this.current.updateRectMinMax(getCurrentTransform(this.ctx), minMax);
1444+
Util.axialAlignedBoundingBox(
1445+
minMax,
1446+
getCurrentTransform(this.ctx),
1447+
this.current.minMax
1448+
);
14621449
this[op](path);
14631450
}
14641451

@@ -2240,10 +2227,9 @@ class CanvasGraphics {
22402227
const inv = getCurrentTransformInverse(ctx);
22412228
if (inv) {
22422229
const { width, height } = ctx.canvas;
2243-
const [x0, y0, x1, y1] = Util.getAxialAlignedBoundingBox(
2244-
[0, 0, width, height],
2245-
inv
2246-
);
2230+
const minMax = MIN_MAX_INIT.slice();
2231+
Util.axialAlignedBoundingBox([0, 0, width, height], inv, minMax);
2232+
const [x0, y0, x1, y1] = minMax;
22472233

22482234
this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
22492235
} else {
@@ -2282,7 +2268,11 @@ class CanvasGraphics {
22822268
this.baseTransform = getCurrentTransform(this.ctx);
22832269

22842270
if (bbox) {
2285-
this.current.updateRectMinMax(this.baseTransform, bbox);
2271+
Util.axialAlignedBoundingBox(
2272+
bbox,
2273+
this.baseTransform,
2274+
this.current.minMax
2275+
);
22862276
const [x0, y0, x1, y1] = bbox;
22872277
const clip = new Path2D();
22882278
clip.rect(x0, y0, x1 - x0, y1 - y0);
@@ -2346,10 +2336,13 @@ class CanvasGraphics {
23462336

23472337
// Based on the current transform figure out how big the bounding box
23482338
// will actually be.
2349-
let bounds = Util.getAxialAlignedBoundingBox(
2339+
let bounds = MIN_MAX_INIT.slice();
2340+
Util.axialAlignedBoundingBox(
23502341
group.bbox,
2351-
getCurrentTransform(currentCtx)
2342+
getCurrentTransform(currentCtx),
2343+
bounds
23522344
);
2345+
23532346
// Clip the bounding box to the current canvas.
23542347
const canvasBounds = [
23552348
0,
@@ -2448,9 +2441,11 @@ class CanvasGraphics {
24482441
this.restore();
24492442
this.ctx.save();
24502443
this.ctx.setTransform(...currentMtx);
2451-
const dirtyBox = Util.getAxialAlignedBoundingBox(
2444+
const dirtyBox = MIN_MAX_INIT.slice();
2445+
Util.axialAlignedBoundingBox(
24522446
[0, 0, groupCtx.canvas.width, groupCtx.canvas.height],
2453-
currentMtx
2447+
currentMtx,
2448+
dirtyBox
24542449
);
24552450
this.ctx.drawImage(groupCtx.canvas, 0, 0);
24562451
this.ctx.restore();

Diff for: src/display/pattern_helper.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -680,12 +680,11 @@ class TilingPattern {
680680
const bboxWidth = x1 - x0;
681681
const bboxHeight = y1 - y0;
682682
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
683-
graphics.current.updateRectMinMax(getCurrentTransform(graphics.ctx), [
684-
x0,
685-
y0,
686-
x1,
687-
y1,
688-
]);
683+
Util.axialAlignedBoundingBox(
684+
[x0, y0, x1, y1],
685+
getCurrentTransform(graphics.ctx),
686+
graphics.current.minMax
687+
);
689688
graphics.clip();
690689
graphics.endPath();
691690
}

Diff for: src/shared/util.js

+57-19
Original file line numberDiff line numberDiff line change
@@ -742,13 +742,20 @@ class Util {
742742

743743
// For 2d affine transforms
744744
static applyTransform(p, m) {
745-
const [p0, p1] = p;
745+
const p0 = p[0];
746+
const p1 = p[1];
746747
p[0] = p0 * m[0] + p1 * m[2] + m[4];
747748
p[1] = p0 * m[1] + p1 * m[3] + m[5];
748749
}
749750

750751
// For 2d affine transforms
751-
static applyTransformToBezier(p, [m0, m1, m2, m3, m4, m5]) {
752+
static applyTransformToBezier(p, transform) {
753+
const m0 = transform[0];
754+
const m1 = transform[1];
755+
const m2 = transform[2];
756+
const m3 = transform[3];
757+
const m4 = transform[4];
758+
const m5 = transform[5];
752759
for (let i = 0; i < 6; i += 2) {
753760
const pI = p[i];
754761
const pI1 = p[i + 1];
@@ -758,29 +765,56 @@ class Util {
758765
}
759766

760767
static applyInverseTransform(p, m) {
761-
const [p0, p1] = p;
768+
const p0 = p[0];
769+
const p1 = p[1];
762770
const d = m[0] * m[3] - m[1] * m[2];
763771
p[0] = (p0 * m[3] - p1 * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
764772
p[1] = (-p0 * m[1] + p1 * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
765773
}
766774

767775
// Applies the transform to the rectangle and finds the minimum axially
768776
// aligned bounding box.
769-
static getAxialAlignedBoundingBox(r, m) {
770-
const p1 = [r[0], r[1]];
771-
Util.applyTransform(p1, m);
772-
const p2 = [r[2], r[3]];
773-
Util.applyTransform(p2, m);
774-
const p3 = [r[0], r[3]];
775-
Util.applyTransform(p3, m);
776-
const p4 = [r[2], r[1]];
777-
Util.applyTransform(p4, m);
778-
return [
779-
Math.min(p1[0], p2[0], p3[0], p4[0]),
780-
Math.min(p1[1], p2[1], p3[1], p4[1]),
781-
Math.max(p1[0], p2[0], p3[0], p4[0]),
782-
Math.max(p1[1], p2[1], p3[1], p4[1]),
783-
];
777+
static axialAlignedBoundingBox(rect, transform, output) {
778+
const m0 = transform[0];
779+
const m1 = transform[1];
780+
const m2 = transform[2];
781+
const m3 = transform[3];
782+
const m4 = transform[4];
783+
const m5 = transform[5];
784+
const r0 = rect[0];
785+
const r1 = rect[1];
786+
const r2 = rect[2];
787+
const r3 = rect[3];
788+
789+
let a0 = m0 * r0 + m4;
790+
let a2 = a0;
791+
let a1 = m0 * r2 + m4;
792+
let a3 = a1;
793+
let b0 = m3 * r1 + m5;
794+
let b2 = b0;
795+
let b1 = m3 * r3 + m5;
796+
let b3 = b1;
797+
798+
if (m1 !== 0 || m2 !== 0) {
799+
// Non-scaling matrix: shouldn't be frequent.
800+
const m1r0 = m1 * r0;
801+
const m1r2 = m1 * r2;
802+
const m2r1 = m2 * r1;
803+
const m2r3 = m2 * r3;
804+
a0 += m2r1;
805+
a3 += m2r1;
806+
a1 += m2r3;
807+
a2 += m2r3;
808+
b0 += m1r0;
809+
b3 += m1r0;
810+
b1 += m1r2;
811+
b2 += m1r2;
812+
}
813+
814+
output[0] = Math.min(output[0], a0, a1, a2, a3);
815+
output[1] = Math.min(output[1], b0, b1, b2, b3);
816+
output[2] = Math.max(output[2], a0, a1, a2, a3);
817+
output[3] = Math.max(output[3], b0, b1, b2, b3);
784818
}
785819

786820
static inverseTransform(m) {
@@ -798,7 +832,11 @@ class Util {
798832
// This calculation uses Singular Value Decomposition.
799833
// The SVD can be represented with formula A = USV. We are interested in the
800834
// matrix S here because it represents the scale values.
801-
static singularValueDecompose2dScale([m0, m1, m2, m3], output) {
835+
static singularValueDecompose2dScale(matrix, output) {
836+
const m0 = matrix[0];
837+
const m1 = matrix[1];
838+
const m2 = matrix[2];
839+
const m3 = matrix[3];
802840
// Multiply matrix m with its transpose.
803841
const a = m0 ** 2 + m1 ** 2;
804842
const b = m0 * m2 + m1 * m3;

0 commit comments

Comments
 (0)