Avoid pattern creation with some basic gradients

Some gradients are represented as patterns in PDF.js, because they
mustn't be affected by the current transform. But in most of the cases,
the gradient is attached to the origin and the current transform is very
basic (dilatation + orthogonal + translation). In those cases, we can
avoid creating the pattern because the gradient is transformed into
another gradient when the inverse transform is applied.
This commit is contained in:
calixteman 2025-12-23 10:35:59 +01:00
parent 9f4db380aa
commit 4ff582acbe
No known key found for this signature in database
GPG Key ID: 0C5442631EE0691F

View File

@ -71,23 +71,51 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
this.matrix = null;
}
_createGradient(ctx) {
isOriginBased() {
return (
this._p0[0] === 0 &&
this._p0[1] === 0 &&
(!this.isRadial() || (this._p1[0] === 0 && this._p1[1] === 0))
);
}
isRadial() {
return this._type === "radial";
}
_createGradient(ctx, transform = null) {
let grad;
let firstPoint = this._p0;
let secondPoint = this._p1;
if (transform) {
firstPoint = firstPoint.slice();
secondPoint = secondPoint.slice();
Util.applyTransform(firstPoint, transform);
Util.applyTransform(secondPoint, transform);
}
if (this._type === "axial") {
grad = ctx.createLinearGradient(
this._p0[0],
this._p0[1],
this._p1[0],
this._p1[1]
firstPoint[0],
firstPoint[1],
secondPoint[0],
secondPoint[1]
);
} else if (this._type === "radial") {
let r0 = this._r0;
let r1 = this._r1;
if (transform) {
const scale = new Float32Array(2);
Util.singularValueDecompose2dScale(transform, scale);
r0 *= scale[0];
r1 *= scale[0];
}
grad = ctx.createRadialGradient(
this._p0[0],
this._p0[1],
this._r0,
this._p1[0],
this._p1[1],
this._r1
firstPoint[0],
firstPoint[1],
r0,
secondPoint[0],
secondPoint[1],
r1
);
}
@ -100,6 +128,32 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
getPattern(ctx, owner, inverse, pathType) {
let pattern;
if (pathType === PathType.STROKE || pathType === PathType.FILL) {
if (this.isOriginBased()) {
let transf = Util.transform(inverse, owner.baseTransform);
if (this.matrix) {
transf = Util.transform(transf, this.matrix);
}
const precision = 1e-3;
const n1 = Math.hypot(transf[0], transf[1]);
const n2 = Math.hypot(transf[2], transf[3]);
const ps = (transf[0] * transf[2] + transf[1] * transf[3]) / (n1 * n2);
if (Math.abs(ps) < precision) {
// The images of the basis vectors are orthogonal.
if (this.isRadial()) {
// If the images of the basis vectors are a square then the
// circles are transformed to circles and we can use a gradient
// directly.
if (Math.abs(n1 - n2) < precision) {
return this._createGradient(ctx, transf);
}
} else {
// The rectangles are transformed to rectangles and we can use a
// gradient directly.
return this._createGradient(ctx, transf);
}
}
}
const ownerBBox = owner.current.getClippedPathBoundingBox(
pathType,
getCurrentTransform(ctx)