Skip to content

Fix CSS linear gradient stroke offsets#506

Merged
wieslawsoltes merged 3 commits into
masterfrom
fix/issue-441-gradient-stroke
May 10, 2026
Merged

Fix CSS linear gradient stroke offsets#506
wieslawsoltes merged 3 commits into
masterfrom
fix/issue-441-gradient-stroke

Conversation

@wieslawsoltes
Copy link
Copy Markdown
Owner

Fix CSS Linear Gradient Stroke Offsets

Summary

Fixes #441 by correcting how SVG gradient stop offsets are converted into shader stop positions. The previous implementation converted each <stop offset> through ToDeviceValue(..., skBounds) and divided by the target bounds width. That works only when the target geometry has non-zero width. For vertical line strokes, the geometry bounds width can be zero, producing NaN stop positions in the retained shader model.

The change now normalizes stop offsets directly from the parsed SVG stop value:

  • percentage offsets are converted from 0..100 to 0..1
  • unitless/user offsets are used as 0..1
  • values are clamped to the SVG stop range

The same helper was added to both the primary model paint path and the retained scene graph paint path so Skia rendering and retained/Avalonia conversion stay consistent.

Changes

  • Updated Svg.Model.Services.PaintingService gradient stop collection to avoid dividing stop offsets by rendered element width.
  • Updated Svg.Skia.SvgScenePaintingService with the same offset normalization for retained scene graph rendering.
  • Added Issue441Tests covering the reported CSS pattern:
    • .cls-3{stroke:url(#linear-gradient);stroke-width:3px;}
    • vertical line stroke using gradientUnits="userSpaceOnUse"
    • six parsed gradient stops with expected finite offsets
    • primary SKSvg.Model shader inspection
    • retained scene graph model shader inspection
    • rendered bitmap check for visible yellow gradient output

Why

Issue #441 reported that linear gradients appeared unsupported in an Avalonia app when rendering an SVG containing a CSS-defined gradient stroke. Current Skia output could still look correct in direct rendering, but the retained model exposed the underlying problem: stop offsets could become NaN for zero-width vertical line bounds. Those invalid offsets are especially problematic for consumers that convert the retained shader model into Avalonia gradient brushes.

Normalizing stop offsets from the SVG stop value matches the semantics of gradient stop positions and avoids geometry-dependent invalid values.

Validation

  • dotnet format Svg.Skia.slnx --no-restore --verify-no-changes --include src/Svg.Model/Services/PaintingService.cs src/Svg.SceneGraph/SvgScenePaintingService.cs tests/Svg.Skia.UnitTests/Issue441Tests.cs
  • dotnet test tests/Svg.Skia.UnitTests/Svg.Skia.UnitTests.csproj -f net10.0 -c Release --no-restore --filter "FullyQualifiedName~Issue441Tests" -v minimal
  • dotnet build Svg.Skia.slnx -c Release
  • dotnet test Svg.Skia.slnx -c Release --no-build

Full solution tests passed with existing skips.

Commits

  • bfe859f78 Fix gradient stop offset normalization
  • d90eedaf9 Add issue 441 gradient stroke regression

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linear gradients appear not to be supported in version 3.2.1

1 participant