Skip to content

Commit 4ff582a

Browse files
committed
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.
1 parent 9f4db38 commit 4ff582a

File tree

1 file changed

+65
-11
lines changed

1 file changed

+65
-11
lines changed

src/display/pattern_helper.js

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,23 +71,51 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
7171
this.matrix = null;
7272
}
7373

74-
_createGradient(ctx) {
74+
isOriginBased() {
75+
return (
76+
this._p0[0] === 0 &&
77+
this._p0[1] === 0 &&
78+
(!this.isRadial() || (this._p1[0] === 0 && this._p1[1] === 0))
79+
);
80+
}
81+
82+
isRadial() {
83+
return this._type === "radial";
84+
}
85+
86+
_createGradient(ctx, transform = null) {
7587
let grad;
88+
let firstPoint = this._p0;
89+
let secondPoint = this._p1;
90+
if (transform) {
91+
firstPoint = firstPoint.slice();
92+
secondPoint = secondPoint.slice();
93+
Util.applyTransform(firstPoint, transform);
94+
Util.applyTransform(secondPoint, transform);
95+
}
7696
if (this._type === "axial") {
7797
grad = ctx.createLinearGradient(
78-
this._p0[0],
79-
this._p0[1],
80-
this._p1[0],
81-
this._p1[1]
98+
firstPoint[0],
99+
firstPoint[1],
100+
secondPoint[0],
101+
secondPoint[1]
82102
);
83103
} else if (this._type === "radial") {
104+
let r0 = this._r0;
105+
let r1 = this._r1;
106+
if (transform) {
107+
const scale = new Float32Array(2);
108+
Util.singularValueDecompose2dScale(transform, scale);
109+
r0 *= scale[0];
110+
r1 *= scale[0];
111+
}
84112
grad = ctx.createRadialGradient(
85-
this._p0[0],
86-
this._p0[1],
87-
this._r0,
88-
this._p1[0],
89-
this._p1[1],
90-
this._r1
113+
firstPoint[0],
114+
firstPoint[1],
115+
r0,
116+
secondPoint[0],
117+
secondPoint[1],
118+
r1
91119
);
92120
}
93121

@@ -100,6 +128,32 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
100128
getPattern(ctx, owner, inverse, pathType) {
101129
let pattern;
102130
if (pathType === PathType.STROKE || pathType === PathType.FILL) {
131+
if (this.isOriginBased()) {
132+
let transf = Util.transform(inverse, owner.baseTransform);
133+
if (this.matrix) {
134+
transf = Util.transform(transf, this.matrix);
135+
}
136+
const precision = 1e-3;
137+
const n1 = Math.hypot(transf[0], transf[1]);
138+
const n2 = Math.hypot(transf[2], transf[3]);
139+
const ps = (transf[0] * transf[2] + transf[1] * transf[3]) / (n1 * n2);
140+
if (Math.abs(ps) < precision) {
141+
// The images of the basis vectors are orthogonal.
142+
if (this.isRadial()) {
143+
// If the images of the basis vectors are a square then the
144+
// circles are transformed to circles and we can use a gradient
145+
// directly.
146+
if (Math.abs(n1 - n2) < precision) {
147+
return this._createGradient(ctx, transf);
148+
}
149+
} else {
150+
// The rectangles are transformed to rectangles and we can use a
151+
// gradient directly.
152+
return this._createGradient(ctx, transf);
153+
}
154+
}
155+
}
156+
103157
const ownerBBox = owner.current.getClippedPathBoundingBox(
104158
pathType,
105159
getCurrentTransform(ctx)

0 commit comments

Comments
 (0)