Skip to content

Commit a4950c0

Browse files
authored
Merge pull request #19721 from calixteman/simplif_singular_decomposition
Simplify singularValueDecompose2dScale in order to make it using less memory
2 parents e8b4ed2 + 6e9fbd9 commit a4950c0

3 files changed

Lines changed: 37 additions & 41 deletions

File tree

src/display/canvas.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ const FULL_CHUNK_HEIGHT = 16;
6363
// creating a new DOMMatrix object each time we need it.
6464
const SCALE_MATRIX = new DOMMatrix();
6565

66+
// Used to get some coordinates.
67+
const XY = new Float32Array(2);
68+
6669
/**
6770
* Overrides certain methods on a 2d ctx so that when they are called they
6871
* will also call the same method on the destCtx. The methods that are
@@ -522,9 +525,9 @@ class CanvasExtraState {
522525
}
523526
// Stroked paths can be outside of the path bounding box by 1/2 the line
524527
// width.
525-
const scale = Util.singularValueDecompose2dScale(transform);
526-
const xStrokePad = (scale[0] * this.lineWidth) / 2;
527-
const yStrokePad = (scale[1] * this.lineWidth) / 2;
528+
Util.singularValueDecompose2dScale(transform, XY);
529+
const xStrokePad = (XY[0] * this.lineWidth) / 2;
530+
const yStrokePad = (XY[1] * this.lineWidth) / 2;
528531
box[0] -= xStrokePad;
529532
box[1] -= yStrokePad;
530533
box[2] += xStrokePad;
@@ -777,15 +780,14 @@ function getImageSmoothingEnabled(transform, interpolate) {
777780
return true;
778781
}
779782

780-
const scale = Util.singularValueDecompose2dScale(transform);
783+
Util.singularValueDecompose2dScale(transform, XY);
781784
// Round to a 32bit float so that `<=` check below will pass for numbers that
782785
// are very close, but not exactly the same 64bit floats.
783-
scale[0] = Math.fround(scale[0]);
784-
scale[1] = Math.fround(scale[1]);
785786
const actualScale = Math.fround(
786787
OutputScale.pixelRatio * PixelsPerInch.PDF_TO_CSS_UNITS
787788
);
788-
return scale[0] <= actualScale && scale[1] <= actualScale;
789+
// `XY` is a Float32Array.
790+
return XY[0] <= actualScale && XY[1] <= actualScale;
789791
}
790792

791793
const LINE_CAP_STYLES = ["butt", "round", "square"];
@@ -1958,12 +1960,12 @@ class CanvasGraphics {
19581960
[a, b, c, d, 0, 0],
19591961
invPatternTransform
19601962
);
1961-
const [sx, sy] = Util.singularValueDecompose2dScale(transf);
1963+
Util.singularValueDecompose2dScale(transf, XY);
19621964

19631965
// Cancel the pattern scaling of the line width.
19641966
// If sx and sy are different, unfortunately we can't do anything and
19651967
// we'll have a rendering bug.
1966-
ctx.lineWidth *= Math.max(sx, sy) / fontSize;
1968+
ctx.lineWidth *= Math.max(XY[0], XY[1]) / fontSize;
19671969
ctx.stroke(
19681970
this.#getScaledPath(path, currentTransform, patternStrokeTransform)
19691971
);
@@ -2639,9 +2641,7 @@ class CanvasGraphics {
26392641
rect[2] = width;
26402642
rect[3] = height;
26412643

2642-
const [scaleX, scaleY] = Util.singularValueDecompose2dScale(
2643-
getCurrentTransform(this.ctx)
2644-
);
2644+
Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx), XY);
26452645
const { viewportScale } = this;
26462646
const canvasWidth = Math.ceil(
26472647
width * this.outputScaleX * viewportScale
@@ -2659,7 +2659,7 @@ class CanvasGraphics {
26592659
this.annotationCanvas.savedCtx = this.ctx;
26602660
this.ctx = context;
26612661
this.ctx.save();
2662-
this.ctx.setTransform(scaleX, 0, 0, -scaleY, 0, height * scaleY);
2662+
this.ctx.setTransform(XY[0], 0, 0, -XY[1], 0, height * XY[1]);
26632663

26642664
resetCtxToDefault(this.ctx);
26652665
} else {

src/display/pattern_helper.js

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -398,16 +398,18 @@ class MeshShadingPattern extends BaseShadingPattern {
398398

399399
getPattern(ctx, owner, inverse, pathType) {
400400
applyBoundingBox(ctx, this._bbox);
401-
let scale;
401+
const scale = new Float32Array(2);
402402
if (pathType === PathType.SHADING) {
403-
scale = Util.singularValueDecompose2dScale(getCurrentTransform(ctx));
404-
} else {
403+
Util.singularValueDecompose2dScale(getCurrentTransform(ctx), scale);
404+
} else if (this.matrix) {
405405
// Obtain scale from matrix and current transformation matrix.
406-
scale = Util.singularValueDecompose2dScale(owner.baseTransform);
407-
if (this.matrix) {
408-
const matrixScale = Util.singularValueDecompose2dScale(this.matrix);
409-
scale = [scale[0] * matrixScale[0], scale[1] * matrixScale[1]];
410-
}
406+
Util.singularValueDecompose2dScale(this.matrix, scale);
407+
const [matrixScaleX, matrixScaleY] = scale;
408+
Util.singularValueDecompose2dScale(owner.baseTransform, scale);
409+
scale[0] *= matrixScaleX;
410+
scale[1] *= matrixScaleY;
411+
} else {
412+
Util.singularValueDecompose2dScale(owner.baseTransform, scale);
411413
}
412414

413415
// Rasterizing on the main thread since sending/queue large canvases
@@ -517,12 +519,12 @@ class TilingPattern {
517519
const height = y1 - y0;
518520

519521
// Obtain scale from matrix and current transformation matrix.
520-
const matrixScale = Util.singularValueDecompose2dScale(this.matrix);
521-
const curMatrixScale = Util.singularValueDecompose2dScale(
522-
this.baseTransform
523-
);
524-
const combinedScaleX = matrixScale[0] * curMatrixScale[0];
525-
const combinedScaleY = matrixScale[1] * curMatrixScale[1];
522+
const scale = new Float32Array(2);
523+
Util.singularValueDecompose2dScale(this.matrix, scale);
524+
const [matrixScaleX, matrixScaleY] = scale;
525+
Util.singularValueDecompose2dScale(this.baseTransform, scale);
526+
const combinedScaleX = matrixScaleX * scale[0];
527+
const combinedScaleY = matrixScaleY * scale[1];
526528

527529
let canvasWidth = width,
528530
canvasHeight = height,

src/shared/util.js

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -732,23 +732,17 @@ class Util {
732732
// This calculation uses Singular Value Decomposition.
733733
// The SVD can be represented with formula A = USV. We are interested in the
734734
// matrix S here because it represents the scale values.
735-
static singularValueDecompose2dScale(m) {
736-
const transpose = [m[0], m[2], m[1], m[3]];
737-
735+
static singularValueDecompose2dScale([m0, m1, m2, m3], output) {
738736
// Multiply matrix m with its transpose.
739-
const a = m[0] * transpose[0] + m[1] * transpose[2];
740-
const b = m[0] * transpose[1] + m[1] * transpose[3];
741-
const c = m[2] * transpose[0] + m[3] * transpose[2];
742-
const d = m[2] * transpose[1] + m[3] * transpose[3];
737+
const a = m0 ** 2 + m1 ** 2;
738+
const b = m0 * m2 + m1 * m3;
739+
const c = m2 ** 2 + m3 ** 2;
743740

744741
// Solve the second degree polynomial to get roots.
745-
const first = (a + d) / 2;
746-
const second = Math.sqrt((a + d) ** 2 - 4 * (a * d - c * b)) / 2;
747-
const sx = first + second || 1;
748-
const sy = first - second || 1;
749-
750-
// Scale values are the square roots of the eigenvalues.
751-
return [Math.sqrt(sx), Math.sqrt(sy)];
742+
const first = (a + c) / 2;
743+
const second = Math.sqrt(first ** 2 - (a * c - b ** 2));
744+
output[0] = Math.sqrt(first + second || 1);
745+
output[1] = Math.sqrt(first - second || 1);
752746
}
753747

754748
// Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)

0 commit comments

Comments
 (0)