diff --git a/src/display/pattern_helper.js b/src/display/pattern_helper.js index 6884eca9c..d460de365 100644 --- a/src/display/pattern_helper.js +++ b/src/display/pattern_helper.js @@ -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)