Merge pull request #19763 from calixteman/simplify_updaterect
Replace UpdateRectMinMax by getAxialAlignedBoundingBox
This commit is contained in:
commit
7eef7dfc78
@ -623,10 +623,9 @@ function getQuadPoints(dict, rect) {
|
|||||||
|
|
||||||
function getTransformMatrix(rect, bbox, matrix) {
|
function getTransformMatrix(rect, bbox, matrix) {
|
||||||
// 12.5.5: Algorithm: Appearance streams
|
// 12.5.5: Algorithm: Appearance streams
|
||||||
const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox(
|
const minMax = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]);
|
||||||
bbox,
|
Util.axialAlignedBoundingBox(bbox, matrix, minMax);
|
||||||
matrix
|
const [minX, minY, maxX, maxY] = minMax;
|
||||||
);
|
|
||||||
if (minX === maxX || minY === maxY) {
|
if (minX === maxX || minY === maxY) {
|
||||||
// From real-life file, bbox was [0, 0, 0, 0]. In this case,
|
// From real-life file, bbox was [0, 0, 0, 0]. In this case,
|
||||||
// just apply the transform for rect
|
// just apply the transform for rect
|
||||||
|
|||||||
@ -63,6 +63,14 @@ const SCALE_MATRIX = new DOMMatrix();
|
|||||||
// Used to get some coordinates.
|
// Used to get some coordinates.
|
||||||
const XY = new Float32Array(2);
|
const XY = new Float32Array(2);
|
||||||
|
|
||||||
|
// Initial rectangle values for the minMax array.
|
||||||
|
const MIN_MAX_INIT = new Float32Array([
|
||||||
|
Infinity,
|
||||||
|
Infinity,
|
||||||
|
-Infinity,
|
||||||
|
-Infinity,
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides certain methods on a 2d ctx so that when they are called they
|
* Overrides certain methods on a 2d ctx so that when they are called they
|
||||||
* will also call the same method on the destCtx. The methods that are
|
* will also call the same method on the destCtx. The methods that are
|
||||||
@ -330,40 +338,19 @@ class CanvasExtraState {
|
|||||||
this.activeSMask = null;
|
this.activeSMask = null;
|
||||||
this.transferMaps = "none";
|
this.transferMaps = "none";
|
||||||
|
|
||||||
this.startNewPathAndClipBox([0, 0, width, height]);
|
this.clipBox = new Float32Array([0, 0, width, height]);
|
||||||
|
this.minMax = MIN_MAX_INIT.slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
const clone = Object.create(this);
|
const clone = Object.create(this);
|
||||||
clone.clipBox = this.clipBox.slice();
|
clone.clipBox = this.clipBox.slice();
|
||||||
|
clone.minMax = this.minMax.slice();
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRectMinMax([m0, m1, m2, m3, m4, m5], [r0, r1, r2, r3]) {
|
|
||||||
const m0r0m4 = m0 * r0 + m4;
|
|
||||||
const m0r2m4 = m0 * r2 + m4;
|
|
||||||
const m1r0m5 = m1 * r0 + m5;
|
|
||||||
const m1r2m5 = m1 * r2 + m5;
|
|
||||||
const m2r1 = m2 * r1;
|
|
||||||
const m2r3 = m2 * r3;
|
|
||||||
const m3r1 = m3 * r1;
|
|
||||||
const m3r3 = m3 * r3;
|
|
||||||
const a0 = m0r0m4 + m2r1;
|
|
||||||
const a1 = m0r2m4 + m2r3;
|
|
||||||
const a2 = m0r0m4 + m2r3;
|
|
||||||
const a3 = m0r2m4 + m2r1;
|
|
||||||
const b0 = m1r0m5 + m3r1;
|
|
||||||
const b1 = m1r2m5 + m3r3;
|
|
||||||
const b2 = m1r0m5 + m3r3;
|
|
||||||
const b3 = m1r2m5 + m3r1;
|
|
||||||
this.minX = Math.min(this.minX, a0, a1, a2, a3);
|
|
||||||
this.maxX = Math.max(this.maxX, a0, a1, a2, a3);
|
|
||||||
this.minY = Math.min(this.minY, b0, b1, b2, b3);
|
|
||||||
this.maxY = Math.max(this.maxY, b0, b1, b2, b3);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPathBoundingBox(pathType = PathType.FILL, transform = null) {
|
getPathBoundingBox(pathType = PathType.FILL, transform = null) {
|
||||||
const box = [this.minX, this.minY, this.maxX, this.maxY];
|
const box = this.minMax.slice();
|
||||||
if (pathType === PathType.STROKE) {
|
if (pathType === PathType.STROKE) {
|
||||||
if (!transform) {
|
if (!transform) {
|
||||||
unreachable("Stroke bounding box must include transform.");
|
unreachable("Stroke bounding box must include transform.");
|
||||||
@ -387,15 +374,12 @@ class CanvasExtraState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isEmptyClip() {
|
isEmptyClip() {
|
||||||
return this.minX === Infinity;
|
return this.minMax[0] === Infinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
startNewPathAndClipBox(box) {
|
startNewPathAndClipBox(box) {
|
||||||
this.clipBox = box;
|
this.clipBox.set(box, 0);
|
||||||
this.minX = Infinity;
|
this.minMax.set(MIN_MAX_INIT, 0);
|
||||||
this.minY = Infinity;
|
|
||||||
this.maxX = 0;
|
|
||||||
this.maxY = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) {
|
getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) {
|
||||||
@ -1014,10 +998,9 @@ class CanvasGraphics {
|
|||||||
0,
|
0,
|
||||||
]);
|
]);
|
||||||
maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
|
maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
|
||||||
const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox(
|
const minMax = MIN_MAX_INIT.slice();
|
||||||
[0, 0, width, height],
|
Util.axialAlignedBoundingBox([0, 0, width, height], maskToCanvas, minMax);
|
||||||
maskToCanvas
|
const [minX, minY, maxX, maxY] = minMax;
|
||||||
);
|
|
||||||
const drawnWidth = Math.round(maxX - minX) || 1;
|
const drawnWidth = Math.round(maxX - minX) || 1;
|
||||||
const drawnHeight = Math.round(maxY - minY) || 1;
|
const drawnHeight = Math.round(maxY - minY) || 1;
|
||||||
const fillCanvas = this.cachedCanvases.getCanvas(
|
const fillCanvas = this.cachedCanvases.getCanvas(
|
||||||
@ -1458,7 +1441,11 @@ class CanvasGraphics {
|
|||||||
}
|
}
|
||||||
path = path2d;
|
path = path2d;
|
||||||
}
|
}
|
||||||
this.current.updateRectMinMax(getCurrentTransform(this.ctx), minMax);
|
Util.axialAlignedBoundingBox(
|
||||||
|
minMax,
|
||||||
|
getCurrentTransform(this.ctx),
|
||||||
|
this.current.minMax
|
||||||
|
);
|
||||||
this[op](path);
|
this[op](path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2240,10 +2227,9 @@ class CanvasGraphics {
|
|||||||
const inv = getCurrentTransformInverse(ctx);
|
const inv = getCurrentTransformInverse(ctx);
|
||||||
if (inv) {
|
if (inv) {
|
||||||
const { width, height } = ctx.canvas;
|
const { width, height } = ctx.canvas;
|
||||||
const [x0, y0, x1, y1] = Util.getAxialAlignedBoundingBox(
|
const minMax = MIN_MAX_INIT.slice();
|
||||||
[0, 0, width, height],
|
Util.axialAlignedBoundingBox([0, 0, width, height], inv, minMax);
|
||||||
inv
|
const [x0, y0, x1, y1] = minMax;
|
||||||
);
|
|
||||||
|
|
||||||
this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
|
this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
|
||||||
} else {
|
} else {
|
||||||
@ -2282,7 +2268,11 @@ class CanvasGraphics {
|
|||||||
this.baseTransform = getCurrentTransform(this.ctx);
|
this.baseTransform = getCurrentTransform(this.ctx);
|
||||||
|
|
||||||
if (bbox) {
|
if (bbox) {
|
||||||
this.current.updateRectMinMax(this.baseTransform, bbox);
|
Util.axialAlignedBoundingBox(
|
||||||
|
bbox,
|
||||||
|
this.baseTransform,
|
||||||
|
this.current.minMax
|
||||||
|
);
|
||||||
const [x0, y0, x1, y1] = bbox;
|
const [x0, y0, x1, y1] = bbox;
|
||||||
const clip = new Path2D();
|
const clip = new Path2D();
|
||||||
clip.rect(x0, y0, x1 - x0, y1 - y0);
|
clip.rect(x0, y0, x1 - x0, y1 - y0);
|
||||||
@ -2346,10 +2336,13 @@ class CanvasGraphics {
|
|||||||
|
|
||||||
// Based on the current transform figure out how big the bounding box
|
// Based on the current transform figure out how big the bounding box
|
||||||
// will actually be.
|
// will actually be.
|
||||||
let bounds = Util.getAxialAlignedBoundingBox(
|
let bounds = MIN_MAX_INIT.slice();
|
||||||
|
Util.axialAlignedBoundingBox(
|
||||||
group.bbox,
|
group.bbox,
|
||||||
getCurrentTransform(currentCtx)
|
getCurrentTransform(currentCtx),
|
||||||
|
bounds
|
||||||
);
|
);
|
||||||
|
|
||||||
// Clip the bounding box to the current canvas.
|
// Clip the bounding box to the current canvas.
|
||||||
const canvasBounds = [
|
const canvasBounds = [
|
||||||
0,
|
0,
|
||||||
@ -2448,9 +2441,11 @@ class CanvasGraphics {
|
|||||||
this.restore();
|
this.restore();
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
this.ctx.setTransform(...currentMtx);
|
this.ctx.setTransform(...currentMtx);
|
||||||
const dirtyBox = Util.getAxialAlignedBoundingBox(
|
const dirtyBox = MIN_MAX_INIT.slice();
|
||||||
|
Util.axialAlignedBoundingBox(
|
||||||
[0, 0, groupCtx.canvas.width, groupCtx.canvas.height],
|
[0, 0, groupCtx.canvas.width, groupCtx.canvas.height],
|
||||||
currentMtx
|
currentMtx,
|
||||||
|
dirtyBox
|
||||||
);
|
);
|
||||||
this.ctx.drawImage(groupCtx.canvas, 0, 0);
|
this.ctx.drawImage(groupCtx.canvas, 0, 0);
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
|
|||||||
@ -680,12 +680,11 @@ class TilingPattern {
|
|||||||
const bboxWidth = x1 - x0;
|
const bboxWidth = x1 - x0;
|
||||||
const bboxHeight = y1 - y0;
|
const bboxHeight = y1 - y0;
|
||||||
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
|
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
|
||||||
graphics.current.updateRectMinMax(getCurrentTransform(graphics.ctx), [
|
Util.axialAlignedBoundingBox(
|
||||||
x0,
|
[x0, y0, x1, y1],
|
||||||
y0,
|
getCurrentTransform(graphics.ctx),
|
||||||
x1,
|
graphics.current.minMax
|
||||||
y1,
|
);
|
||||||
]);
|
|
||||||
graphics.clip();
|
graphics.clip();
|
||||||
graphics.endPath();
|
graphics.endPath();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -742,13 +742,20 @@ class Util {
|
|||||||
|
|
||||||
// For 2d affine transforms
|
// For 2d affine transforms
|
||||||
static applyTransform(p, m) {
|
static applyTransform(p, m) {
|
||||||
const [p0, p1] = p;
|
const p0 = p[0];
|
||||||
|
const p1 = p[1];
|
||||||
p[0] = p0 * m[0] + p1 * m[2] + m[4];
|
p[0] = p0 * m[0] + p1 * m[2] + m[4];
|
||||||
p[1] = p0 * m[1] + p1 * m[3] + m[5];
|
p[1] = p0 * m[1] + p1 * m[3] + m[5];
|
||||||
}
|
}
|
||||||
|
|
||||||
// For 2d affine transforms
|
// For 2d affine transforms
|
||||||
static applyTransformToBezier(p, [m0, m1, m2, m3, m4, m5]) {
|
static applyTransformToBezier(p, transform) {
|
||||||
|
const m0 = transform[0];
|
||||||
|
const m1 = transform[1];
|
||||||
|
const m2 = transform[2];
|
||||||
|
const m3 = transform[3];
|
||||||
|
const m4 = transform[4];
|
||||||
|
const m5 = transform[5];
|
||||||
for (let i = 0; i < 6; i += 2) {
|
for (let i = 0; i < 6; i += 2) {
|
||||||
const pI = p[i];
|
const pI = p[i];
|
||||||
const pI1 = p[i + 1];
|
const pI1 = p[i + 1];
|
||||||
@ -758,7 +765,8 @@ class Util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static applyInverseTransform(p, m) {
|
static applyInverseTransform(p, m) {
|
||||||
const [p0, p1] = p;
|
const p0 = p[0];
|
||||||
|
const p1 = p[1];
|
||||||
const d = m[0] * m[3] - m[1] * m[2];
|
const d = m[0] * m[3] - m[1] * m[2];
|
||||||
p[0] = (p0 * m[3] - p1 * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
|
p[0] = (p0 * m[3] - p1 * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
|
||||||
p[1] = (-p0 * m[1] + p1 * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
|
p[1] = (-p0 * m[1] + p1 * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
|
||||||
@ -766,21 +774,47 @@ class Util {
|
|||||||
|
|
||||||
// Applies the transform to the rectangle and finds the minimum axially
|
// Applies the transform to the rectangle and finds the minimum axially
|
||||||
// aligned bounding box.
|
// aligned bounding box.
|
||||||
static getAxialAlignedBoundingBox(r, m) {
|
static axialAlignedBoundingBox(rect, transform, output) {
|
||||||
const p1 = [r[0], r[1]];
|
const m0 = transform[0];
|
||||||
Util.applyTransform(p1, m);
|
const m1 = transform[1];
|
||||||
const p2 = [r[2], r[3]];
|
const m2 = transform[2];
|
||||||
Util.applyTransform(p2, m);
|
const m3 = transform[3];
|
||||||
const p3 = [r[0], r[3]];
|
const m4 = transform[4];
|
||||||
Util.applyTransform(p3, m);
|
const m5 = transform[5];
|
||||||
const p4 = [r[2], r[1]];
|
const r0 = rect[0];
|
||||||
Util.applyTransform(p4, m);
|
const r1 = rect[1];
|
||||||
return [
|
const r2 = rect[2];
|
||||||
Math.min(p1[0], p2[0], p3[0], p4[0]),
|
const r3 = rect[3];
|
||||||
Math.min(p1[1], p2[1], p3[1], p4[1]),
|
|
||||||
Math.max(p1[0], p2[0], p3[0], p4[0]),
|
let a0 = m0 * r0 + m4;
|
||||||
Math.max(p1[1], p2[1], p3[1], p4[1]),
|
let a2 = a0;
|
||||||
];
|
let a1 = m0 * r2 + m4;
|
||||||
|
let a3 = a1;
|
||||||
|
let b0 = m3 * r1 + m5;
|
||||||
|
let b2 = b0;
|
||||||
|
let b1 = m3 * r3 + m5;
|
||||||
|
let b3 = b1;
|
||||||
|
|
||||||
|
if (m1 !== 0 || m2 !== 0) {
|
||||||
|
// Non-scaling matrix: shouldn't be frequent.
|
||||||
|
const m1r0 = m1 * r0;
|
||||||
|
const m1r2 = m1 * r2;
|
||||||
|
const m2r1 = m2 * r1;
|
||||||
|
const m2r3 = m2 * r3;
|
||||||
|
a0 += m2r1;
|
||||||
|
a3 += m2r1;
|
||||||
|
a1 += m2r3;
|
||||||
|
a2 += m2r3;
|
||||||
|
b0 += m1r0;
|
||||||
|
b3 += m1r0;
|
||||||
|
b1 += m1r2;
|
||||||
|
b2 += m1r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[0] = Math.min(output[0], a0, a1, a2, a3);
|
||||||
|
output[1] = Math.min(output[1], b0, b1, b2, b3);
|
||||||
|
output[2] = Math.max(output[2], a0, a1, a2, a3);
|
||||||
|
output[3] = Math.max(output[3], b0, b1, b2, b3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inverseTransform(m) {
|
static inverseTransform(m) {
|
||||||
@ -798,7 +832,11 @@ class Util {
|
|||||||
// This calculation uses Singular Value Decomposition.
|
// This calculation uses Singular Value Decomposition.
|
||||||
// The SVD can be represented with formula A = USV. We are interested in the
|
// The SVD can be represented with formula A = USV. We are interested in the
|
||||||
// matrix S here because it represents the scale values.
|
// matrix S here because it represents the scale values.
|
||||||
static singularValueDecompose2dScale([m0, m1, m2, m3], output) {
|
static singularValueDecompose2dScale(matrix, output) {
|
||||||
|
const m0 = matrix[0];
|
||||||
|
const m1 = matrix[1];
|
||||||
|
const m2 = matrix[2];
|
||||||
|
const m3 = matrix[3];
|
||||||
// Multiply matrix m with its transpose.
|
// Multiply matrix m with its transpose.
|
||||||
const a = m0 ** 2 + m1 ** 2;
|
const a = m0 ** 2 + m1 ** 2;
|
||||||
const b = m0 * m2 + m1 * m3;
|
const b = m0 * m2 + m1 * m3;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user