Fix radial gradient when the two circles have an intersection

Fix #257.
This commit is contained in:
Calixte Denizet 2026-03-31 21:47:24 +02:00
parent 399fce6471
commit b42dd39aae
No known key found for this signature in database
GPG Key ID: 0C5442631EE0691F
2 changed files with 19 additions and 11 deletions

View File

@ -84,21 +84,19 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
return this._type === "radial"; return this._type === "radial";
} }
// Returns true when the smaller circle's center (p0 when r0 ≤ r1) lies // Return true when the a circle of a radial gradient isn't fully included in
// outside the larger circle. In that case the canvas radial gradient picks // the other circle, which means that the gradient is conic and we need to
// t > 1 solutions for points inside the outer circle and maps them to the // draw the reversed gradient first to correctly render the pattern (see
// transparent stop we append for extendEnd=false, making the gradient // #20851 and #257).
// invisible. A two-pass draw (reversed first, normal on top) fixes this areConic() {
// (see #20851). if (!this.isRadial()) {
_isCircleCenterOutside() {
if (!this.isRadial() || this._r0 > this._r1) {
return false; return false;
} }
const dist = Math.hypot( const dist = Math.hypot(
this._p0[0] - this._p1[0], this._p0[0] - this._p1[0],
this._p0[1] - this._p1[1] this._p0[1] - this._p1[1]
); );
return dist > this._r1; return dist + this._r1 > this._r0 && dist + this._r0 > this._r1;
} }
_createGradient(ctx, transform = null) { _createGradient(ctx, transform = null) {
@ -242,7 +240,7 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
} }
applyBoundingBox(tmpCtx, this._bbox); applyBoundingBox(tmpCtx, this._bbox);
if (this._isCircleCenterOutside()) { if (this.areConic()) {
tmpCtx.fillStyle = this._createReversedGradient(tmpCtx); tmpCtx.fillStyle = this._createReversedGradient(tmpCtx);
tmpCtx.fill(); tmpCtx.fill();
} }
@ -257,7 +255,7 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
// Shading fills are applied relative to the current matrix which is also // Shading fills are applied relative to the current matrix which is also
// how canvas gradients work, so there's no need to do anything special // how canvas gradients work, so there's no need to do anything special
// here. // here.
if (this._isCircleCenterOutside()) { if (this.areConic()) {
// Draw the reversed gradient first so the normal gradient can // Draw the reversed gradient first so the normal gradient can
// correctly overlay it (see _isCircleCenterOutside for details). // correctly overlay it (see _isCircleCenterOutside for details).
ctx.save(); ctx.save();

View File

@ -14042,5 +14042,15 @@
"md5": "7796f0131e7d6428c1bf24a73ff13f95", "md5": "7796f0131e7d6428c1bf24a73ff13f95",
"rounds": 1, "rounds": 1,
"type": "eq" "type": "eq"
},
{
"id": "pdfspec-radial-gradient",
"file": "pdfs/pdf.pdf",
"md5": "dbdb23c939d2be09b43126c3c56060c7",
"link": true,
"firstPage": 1144,
"lastPage": 1144,
"rounds": 1,
"type": "eq"
} }
] ]