Reduce memory use and improve perfs when computing the bounding box of a bezier curve (bug 1875547)
It isn't really a fix for the mentioned bug but it slightly improve things. In reducing the memory use, the time spent in the GC is reduced either. The algorithm to compute the bounding box is the same as before but it has just been rewritten to be more efficient.
This commit is contained in:
parent
a5d4660a75
commit
7f2428a77e
@ -1386,17 +1386,17 @@ class PartialEvaluator {
|
||||
const y = args[1] + args[3];
|
||||
minMax = [
|
||||
Math.min(args[0], x),
|
||||
Math.max(args[0], x),
|
||||
Math.min(args[1], y),
|
||||
Math.max(args[0], x),
|
||||
Math.max(args[1], y),
|
||||
];
|
||||
break;
|
||||
case OPS.moveTo:
|
||||
case OPS.lineTo:
|
||||
minMax = [args[0], args[0], args[1], args[1]];
|
||||
minMax = [args[0], args[1], args[0], args[1]];
|
||||
break;
|
||||
default:
|
||||
minMax = [Infinity, -Infinity, Infinity, -Infinity];
|
||||
minMax = [Infinity, Infinity, -Infinity, -Infinity];
|
||||
break;
|
||||
}
|
||||
operatorList.addOp(OPS.constructPath, [[fn], args, minMax]);
|
||||
@ -1420,15 +1420,15 @@ class PartialEvaluator {
|
||||
const x = args[0] + args[2];
|
||||
const y = args[1] + args[3];
|
||||
minMax[0] = Math.min(minMax[0], args[0], x);
|
||||
minMax[1] = Math.max(minMax[1], args[0], x);
|
||||
minMax[2] = Math.min(minMax[2], args[1], y);
|
||||
minMax[1] = Math.min(minMax[1], args[1], y);
|
||||
minMax[2] = Math.max(minMax[2], args[0], x);
|
||||
minMax[3] = Math.max(minMax[3], args[1], y);
|
||||
break;
|
||||
case OPS.moveTo:
|
||||
case OPS.lineTo:
|
||||
minMax[0] = Math.min(minMax[0], args[0]);
|
||||
minMax[1] = Math.max(minMax[1], args[0]);
|
||||
minMax[2] = Math.min(minMax[2], args[1]);
|
||||
minMax[1] = Math.min(minMax[1], args[1]);
|
||||
minMax[2] = Math.max(minMax[2], args[0]);
|
||||
minMax[3] = Math.max(minMax[3], args[1]);
|
||||
break;
|
||||
}
|
||||
|
@ -529,18 +529,14 @@ class CanvasExtraState {
|
||||
updateScalingPathMinMax(transform, minMax) {
|
||||
Util.scaleMinMax(transform, minMax);
|
||||
this.minX = Math.min(this.minX, minMax[0]);
|
||||
this.maxX = Math.max(this.maxX, minMax[1]);
|
||||
this.minY = Math.min(this.minY, minMax[2]);
|
||||
this.minY = Math.min(this.minY, minMax[1]);
|
||||
this.maxX = Math.max(this.maxX, minMax[2]);
|
||||
this.maxY = Math.max(this.maxY, minMax[3]);
|
||||
}
|
||||
|
||||
updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
|
||||
const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3);
|
||||
const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax);
|
||||
if (minMax) {
|
||||
minMax[0] = Math.min(minMax[0], box[0], box[2]);
|
||||
minMax[1] = Math.max(minMax[1], box[0], box[2]);
|
||||
minMax[2] = Math.min(minMax[2], box[1], box[3]);
|
||||
minMax[3] = Math.max(minMax[3], box[1], box[3]);
|
||||
return;
|
||||
}
|
||||
this.updateRectMinMax(transform, box);
|
||||
|
@ -651,52 +651,52 @@ class Util {
|
||||
|
||||
// Apply a scaling matrix to some min/max values.
|
||||
// If a scaling factor is negative then min and max must be
|
||||
// swaped.
|
||||
// swapped.
|
||||
static scaleMinMax(transform, minMax) {
|
||||
let temp;
|
||||
if (transform[0]) {
|
||||
if (transform[0] < 0) {
|
||||
temp = minMax[0];
|
||||
minMax[0] = minMax[1];
|
||||
minMax[1] = temp;
|
||||
}
|
||||
minMax[0] *= transform[0];
|
||||
minMax[1] *= transform[0];
|
||||
|
||||
if (transform[3] < 0) {
|
||||
temp = minMax[2];
|
||||
minMax[2] = minMax[3];
|
||||
minMax[3] = temp;
|
||||
}
|
||||
minMax[2] *= transform[3];
|
||||
minMax[3] *= transform[3];
|
||||
} else {
|
||||
temp = minMax[0];
|
||||
minMax[0] = minMax[2];
|
||||
minMax[2] = temp;
|
||||
}
|
||||
minMax[0] *= transform[0];
|
||||
minMax[2] *= transform[0];
|
||||
|
||||
if (transform[3] < 0) {
|
||||
temp = minMax[1];
|
||||
minMax[1] = minMax[3];
|
||||
minMax[3] = temp;
|
||||
|
||||
if (transform[1] < 0) {
|
||||
}
|
||||
minMax[1] *= transform[3];
|
||||
minMax[3] *= transform[3];
|
||||
} else {
|
||||
temp = minMax[0];
|
||||
minMax[0] = minMax[1];
|
||||
minMax[1] = temp;
|
||||
temp = minMax[2];
|
||||
minMax[2] = minMax[3];
|
||||
minMax[3] = temp;
|
||||
|
||||
if (transform[1] < 0) {
|
||||
temp = minMax[1];
|
||||
minMax[1] = minMax[3];
|
||||
minMax[3] = temp;
|
||||
}
|
||||
minMax[2] *= transform[1];
|
||||
minMax[1] *= transform[1];
|
||||
minMax[3] *= transform[1];
|
||||
|
||||
if (transform[2] < 0) {
|
||||
temp = minMax[0];
|
||||
minMax[0] = minMax[1];
|
||||
minMax[1] = temp;
|
||||
minMax[0] = minMax[2];
|
||||
minMax[2] = temp;
|
||||
}
|
||||
minMax[0] *= transform[2];
|
||||
minMax[1] *= transform[2];
|
||||
minMax[2] *= transform[2];
|
||||
}
|
||||
minMax[0] += transform[4];
|
||||
minMax[1] += transform[4];
|
||||
minMax[2] += transform[5];
|
||||
minMax[1] += transform[5];
|
||||
minMax[2] += transform[4];
|
||||
minMax[3] += transform[5];
|
||||
}
|
||||
|
||||
@ -822,77 +822,117 @@ class Util {
|
||||
return [xLow, yLow, xHigh, yHigh];
|
||||
}
|
||||
|
||||
// From https://github.com/adobe-webplatform/Snap.svg/blob/b365287722a72526000ac4bfcf0ce4cac2faa015/src/path.js#L852
|
||||
static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3) {
|
||||
const tvalues = [],
|
||||
bounds = [[], []];
|
||||
let a, b, c, t, t1, t2, b2ac, sqrtb2ac;
|
||||
for (let i = 0; i < 2; ++i) {
|
||||
if (i === 0) {
|
||||
b = 6 * x0 - 12 * x1 + 6 * x2;
|
||||
a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
|
||||
c = 3 * x1 - 3 * x0;
|
||||
} else {
|
||||
b = 6 * y0 - 12 * y1 + 6 * y2;
|
||||
a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
|
||||
c = 3 * y1 - 3 * y0;
|
||||
static #getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, t, minMax) {
|
||||
if (t <= 0 || t >= 1) {
|
||||
return;
|
||||
}
|
||||
const mt = 1 - t;
|
||||
const tt = t * t;
|
||||
const ttt = tt * t;
|
||||
const x = mt * (mt * (mt * x0 + 3 * t * x1) + 3 * tt * x2) + ttt * x3;
|
||||
const y = mt * (mt * (mt * y0 + 3 * t * y1) + 3 * tt * y2) + ttt * y3;
|
||||
minMax[0] = Math.min(minMax[0], x);
|
||||
minMax[1] = Math.min(minMax[1], y);
|
||||
minMax[2] = Math.max(minMax[2], x);
|
||||
minMax[3] = Math.max(minMax[3], y);
|
||||
}
|
||||
|
||||
static #getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, a, b, c, minMax) {
|
||||
if (Math.abs(a) < 1e-12) {
|
||||
if (Math.abs(b) < 1e-12) {
|
||||
continue;
|
||||
}
|
||||
t = -c / b;
|
||||
if (0 < t && t < 1) {
|
||||
tvalues.push(t);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
b2ac = b * b - 4 * c * a;
|
||||
sqrtb2ac = Math.sqrt(b2ac);
|
||||
if (b2ac < 0) {
|
||||
continue;
|
||||
}
|
||||
t1 = (-b + sqrtb2ac) / (2 * a);
|
||||
if (0 < t1 && t1 < 1) {
|
||||
tvalues.push(t1);
|
||||
}
|
||||
t2 = (-b - sqrtb2ac) / (2 * a);
|
||||
if (0 < t2 && t2 < 1) {
|
||||
tvalues.push(t2);
|
||||
if (Math.abs(b) >= 1e-12) {
|
||||
this.#getExtremumOnCurve(
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
y0,
|
||||
y1,
|
||||
y2,
|
||||
y3,
|
||||
-c / b,
|
||||
minMax
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let j = tvalues.length,
|
||||
mt;
|
||||
const jlen = j;
|
||||
while (j--) {
|
||||
t = tvalues[j];
|
||||
mt = 1 - t;
|
||||
bounds[0][j] =
|
||||
mt * mt * mt * x0 +
|
||||
3 * mt * mt * t * x1 +
|
||||
3 * mt * t * t * x2 +
|
||||
t * t * t * x3;
|
||||
bounds[1][j] =
|
||||
mt * mt * mt * y0 +
|
||||
3 * mt * mt * t * y1 +
|
||||
3 * mt * t * t * y2 +
|
||||
t * t * t * y3;
|
||||
const delta = b ** 2 - 4 * c * a;
|
||||
if (delta < 0) {
|
||||
return;
|
||||
}
|
||||
const sqrtDelta = Math.sqrt(delta);
|
||||
const a2 = 2 * a;
|
||||
this.#getExtremumOnCurve(
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
y0,
|
||||
y1,
|
||||
y2,
|
||||
y3,
|
||||
(-b + sqrtDelta) / a2,
|
||||
minMax
|
||||
);
|
||||
this.#getExtremumOnCurve(
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
y0,
|
||||
y1,
|
||||
y2,
|
||||
y3,
|
||||
(-b - sqrtDelta) / a2,
|
||||
minMax
|
||||
);
|
||||
}
|
||||
|
||||
bounds[0][jlen] = x0;
|
||||
bounds[1][jlen] = y0;
|
||||
bounds[0][jlen + 1] = x3;
|
||||
bounds[1][jlen + 1] = y3;
|
||||
bounds[0].length = bounds[1].length = jlen + 2;
|
||||
|
||||
return [
|
||||
Math.min(...bounds[0]),
|
||||
Math.min(...bounds[1]),
|
||||
Math.max(...bounds[0]),
|
||||
Math.max(...bounds[1]),
|
||||
// From https://github.com/adobe-webplatform/Snap.svg/blob/b365287722a72526000ac4bfcf0ce4cac2faa015/src/path.js#L852
|
||||
static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
|
||||
if (minMax) {
|
||||
minMax[0] = Math.min(minMax[0], x0, x3);
|
||||
minMax[1] = Math.min(minMax[1], y0, y3);
|
||||
minMax[2] = Math.max(minMax[2], x0, x3);
|
||||
minMax[3] = Math.max(minMax[3], y0, y3);
|
||||
} else {
|
||||
minMax = [
|
||||
Math.min(x0, x3),
|
||||
Math.min(y0, y3),
|
||||
Math.max(x0, x3),
|
||||
Math.max(y0, y3),
|
||||
];
|
||||
}
|
||||
this.#getExtremum(
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
y0,
|
||||
y1,
|
||||
y2,
|
||||
y3,
|
||||
3 * (-x0 + 3 * (x1 - x2) + x3),
|
||||
6 * (x0 - 2 * x1 + x2),
|
||||
3 * (x1 - x0),
|
||||
minMax
|
||||
);
|
||||
this.#getExtremum(
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
y0,
|
||||
y1,
|
||||
y2,
|
||||
y3,
|
||||
3 * (-y0 + 3 * (y1 - y2) + y3),
|
||||
6 * (y0 - 2 * y1 + y2),
|
||||
3 * (y1 - y0),
|
||||
minMax
|
||||
);
|
||||
return minMax;
|
||||
}
|
||||
}
|
||||
|
||||
const PDFStringTranslateTable = [
|
||||
|
@ -4548,7 +4548,7 @@ describe("annotation", function () {
|
||||
expect(opList.argsArray[5][0]).toEqual([OPS.moveTo, OPS.curveTo]);
|
||||
expect(opList.argsArray[5][1]).toEqual([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
// Min-max.
|
||||
expect(opList.argsArray[5][2]).toEqual([1, 1, 2, 2]);
|
||||
expect(opList.argsArray[5][2]).toEqual([1, 2, 1, 2]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -771,7 +771,7 @@ describe("api", function () {
|
||||
[
|
||||
[OPS.moveTo, OPS.lineTo],
|
||||
[0, 9.75, 0.5, 9.75],
|
||||
[0, 0.5, 9.75, 9.75],
|
||||
[0, 9.75, 0.5, 9.75],
|
||||
],
|
||||
null,
|
||||
]);
|
||||
|
Loading…
Reference in New Issue
Block a user