Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
076e0a2
Update SVG
wieslawsoltes Apr 2, 2026
f56b3d6
Add animation implementation plan
wieslawsoltes Apr 3, 2026
bccf501
Add SVG animation object model
wieslawsoltes Apr 3, 2026
a3fab42
Add shared SVG interaction and hit testing
wieslawsoltes Apr 3, 2026
a210c79
Add shared SVG animation runtime
wieslawsoltes Apr 3, 2026
f09e112
Polish Avalonia animation sample
wieslawsoltes Apr 3, 2026
c9ad3ee
Format animation-related files
wieslawsoltes Apr 3, 2026
71d07b0
Cache static animation layers
wieslawsoltes Apr 3, 2026
d6e4409
Add animation benchmark harness
wieslawsoltes Apr 3, 2026
25f2f18
Add native composition animation backend
wieslawsoltes Apr 3, 2026
fb431ff
Add Avalonia native composition host
wieslawsoltes Apr 3, 2026
71a6f41
Expose native composition in TestApp
wieslawsoltes Apr 3, 2026
c5e0f7a
Update native composition roadmap
wieslawsoltes Apr 3, 2026
4eecf0f
Add interaction and animation docs
wieslawsoltes Apr 3, 2026
1ea629e
Document control animation backends
wieslawsoltes Apr 3, 2026
982e46b
Refresh top-level animation docs
wieslawsoltes Apr 3, 2026
f106e50
Implement subtree animation invalidation
wieslawsoltes Apr 3, 2026
a736db8
Prefer default animation backends
wieslawsoltes Apr 3, 2026
20058d0
Fix animation timing and additive state
wieslawsoltes Apr 3, 2026
9e5c315
Gate native composition to renderable roots
wieslawsoltes Apr 3, 2026
7c3b5ed
Add retained scene graph foundation
wieslawsoltes Apr 4, 2026
9e8bccf
Migrate editor workflows to retained nodes
wieslawsoltes Apr 4, 2026
65c2384
Route interaction through retained scene
wieslawsoltes Apr 4, 2026
094d28e
Update retained rewrite spec
wieslawsoltes Apr 4, 2026
12a96d9
Add remaining scene graph work plan
wieslawsoltes Apr 4, 2026
40455f9
Use retained scene for editor commands
wieslawsoltes Apr 4, 2026
8501af6
Handle indefinite repeat duration
wieslawsoltes Apr 4, 2026
55cfabd
Fix referenced clip path hit testing
wieslawsoltes Apr 4, 2026
3cccc3c
Dispose deferred animation pictures on fallback
wieslawsoltes Apr 4, 2026
279d2dd
Resolve motion percentages against viewport
wieslawsoltes Apr 4, 2026
a560eea
Ignore empty mask container hits
wieslawsoltes Apr 4, 2026
a74fe0e
Preserve descendant opacity in native composition
wieslawsoltes Apr 4, 2026
796370f
Use retained scenes for animation fallback
wieslawsoltes Apr 4, 2026
b8b1cbe
Move editor overlays to retained scene data
wieslawsoltes Apr 4, 2026
6e86c57
Remove retained compiler drawable bridge
wieslawsoltes Apr 4, 2026
41f4b71
Fix retained mask hit testing
wieslawsoltes Apr 4, 2026
ec458f6
Fix animation timing edge cases
wieslawsoltes Apr 4, 2026
537eb2e
Fix event timing instance handling
wieslawsoltes Apr 4, 2026
7865aab
Prune hidden retained hit-test subtrees
wieslawsoltes Apr 4, 2026
1cf1438
Remove drawable editor fallbacks
wieslawsoltes Apr 4, 2026
aaa1666
Migrate retained animation runtime APIs
wieslawsoltes Apr 4, 2026
60ac328
Update retained scene graph roadmap
wieslawsoltes Apr 4, 2026
0bba357
Remove animation reflection access
wieslawsoltes Apr 4, 2026
60767e4
Drop obsolete retained API shims
wieslawsoltes Apr 4, 2026
8aeba25
Remove scene compiler reflection
wieslawsoltes Apr 4, 2026
5fc313d
Split retained scene graph project
wieslawsoltes Apr 4, 2026
51b4c3c
Fix trim annotations in SKSvg
wieslawsoltes Apr 4, 2026
754035c
Cut over rendering to retained scenes
wieslawsoltes Apr 5, 2026
eb990cc
Fix retained filter parity and hit testing
wieslawsoltes Apr 5, 2026
e06ca06
Split animation runtime into Svg.Animation
wieslawsoltes Apr 5, 2026
4fd9761
Fix root viewBox animation invalidation
wieslawsoltes Apr 5, 2026
c95476f
Share TestApp core across Avalonia and Uno
wieslawsoltes Apr 5, 2026
8beb031
Fix Uno animated picture lifetime
wieslawsoltes Apr 5, 2026
1a9503c
Update PR summary
wieslawsoltes Apr 5, 2026
124c222
Guard retained embedded SVG image cycles
wieslawsoltes Apr 5, 2026
25a833f
Track nested SVG image document references
wieslawsoltes Apr 5, 2026
60e4a19
Add W3C image cycle regression test
wieslawsoltes Apr 5, 2026
92637cc
Avoid recursive retained mask traversal
wieslawsoltes Apr 5, 2026
c160db8
Add recursive mask regression test
wieslawsoltes Apr 5, 2026
68d9749
Extract animation parsing helpers
wieslawsoltes Apr 5, 2026
9b0c8a4
Add animation parsing regression tests
wieslawsoltes Apr 5, 2026
0a3e9d2
Fix embedded SVG data URI identity
wieslawsoltes Apr 5, 2026
e226d8e
Avoid duplicate embedded image visual state
wieslawsoltes Apr 5, 2026
e350434
Ignore hidden nodes in retained bounds
wieslawsoltes Apr 5, 2026
9e86d69
Suppress unresolved retained filters
wieslawsoltes Apr 5, 2026
8b7d52e
Keep event bridge on source document
wieslawsoltes Apr 5, 2026
17c72a0
Fix animation timing edge cases
wieslawsoltes Apr 5, 2026
9d70113
Clamp invalid animation playback rates
wieslawsoltes Apr 5, 2026
547c2b0
Harden animation parser edge cases
wieslawsoltes Apr 5, 2026
b7ce74c
Disable caching for root viewBox animations
wieslawsoltes Apr 5, 2026
d6f4d3f
Restore retained positioned text rendering
wieslawsoltes Apr 5, 2026
3fe70d7
Add retained positioned text regression test
wieslawsoltes Apr 5, 2026
1767dfb
Remove legacy drawable renderer
wieslawsoltes Apr 6, 2026
d6db2da
Rename retained nodes as renderable
wieslawsoltes Apr 6, 2026
f1ad9cd
Rename editor selection scene state
wieslawsoltes Apr 6, 2026
9499af6
Finalize drawable removal plan
wieslawsoltes Apr 6, 2026
db6735b
Update .gitignore
wieslawsoltes Apr 6, 2026
686ea34
Fix retained document cull rect fallback
wieslawsoltes Apr 6, 2026
15a9101
Normalize retained root SVG viewports
wieslawsoltes Apr 6, 2026
cf1c1a8
Add root viewport regression tests
wieslawsoltes Apr 6, 2026
eb2b7cb
Enable Skia-backed headless capture
wieslawsoltes Apr 6, 2026
c580de7
Add headless SVG viewport regressions
wieslawsoltes Apr 6, 2026
93bd468
Limit W3C image tests to macOS
wieslawsoltes Apr 6, 2026
344b205
Limit resvg image tests to macOS
wieslawsoltes Apr 6, 2026
fbf0734
Preserve blank SVG export bounds
wieslawsoltes Apr 6, 2026
ae79c5f
Normalize standalone SVG viewports
wieslawsoltes Apr 6, 2026
743db1e
Enable W3C fragment image tests
wieslawsoltes Apr 6, 2026
9b8b37e
Make standalone SVG viewport explicit
wieslawsoltes Apr 6, 2026
5801301
Use Chrome baselines for W3C tests
wieslawsoltes Apr 6, 2026
2c34a39
Move marker shorthand support to Svg.Custom
wieslawsoltes Apr 6, 2026
d47fcf1
Update .gitignore
wieslawsoltes Apr 7, 2026
2cc07e9
Improve retained scene parity
wieslawsoltes Apr 7, 2026
e34181b
Document Chrome override workflow
wieslawsoltes Apr 7, 2026
68b62dc
Refresh W3C Chrome baselines
wieslawsoltes Apr 7, 2026
851068e
Normalize sample using order
wieslawsoltes Apr 7, 2026
13f8047
Update SVG
wieslawsoltes Apr 7, 2026
63214aa
Fix retained use and image rendering
wieslawsoltes Apr 7, 2026
3ec1c2d
Move SVG compatibility fixes into Svg.Custom
wieslawsoltes Apr 7, 2026
17f4764
Enable struct W3C coverage
wieslawsoltes Apr 7, 2026
f2bc1cf
Update .gitignore
wieslawsoltes Apr 7, 2026
805d92b
Fix gradient stop inheritance semantics
wieslawsoltes Apr 7, 2026
af1a8ba
Enable W3C paint server baselines
wieslawsoltes Apr 7, 2026
34deb36
Relax retained text bounds assertion
wieslawsoltes Apr 7, 2026
12bfb70
Handle implicit-size SVG image viewports
wieslawsoltes Apr 7, 2026
1f7cfab
Enable W3C coords Chrome baselines
wieslawsoltes Apr 7, 2026
3ea2522
Fix retained filter semantics
wieslawsoltes Apr 7, 2026
0b507f3
Enable W3C filter baselines
wieslawsoltes Apr 7, 2026
cf7d85c
Skip conflicting resvg filter case
wieslawsoltes Apr 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,6 @@ __pycache__/

# macOS
.DS_Store
/output
/.playwright-cli
TestApp.json
17 changes: 17 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ This repository contains the Svg.Skia library and associated tests. The followin
3. Build the solution using `dotnet build Svg.Skia.slnx -c Release`.
4. Run all tests with `dotnet test Svg.Skia.slnx -c Release`.

## W3C Chrome Overrides
- Files under `tests/Svg.Skia.UnitTests/ChromeReference/W3C/` must be generated from real Google Chrome captures, not copied from the legacy W3C PNG set.
- Use `node scripts/capture_w3c_chrome_overrides.mjs` to regenerate the checked Chrome override set. Pass specific test names as comma-separated arguments to limit capture scope, for example `node scripts/capture_w3c_chrome_overrides.mjs masking-path-04-b,linking-a-09-b`.
- The capture script serves the repository over HTTP and screenshots a `480x360` Chrome-hosted iframe, which matches the standalone viewport policy used by `W3CTestSuiteTests`.
- When a Chrome override exists, keep `W3CTestSuiteTests` pointed at that override instead of the W3C reference PNG.
- Do not reintroduce footer exclusion regions for W3C comparisons. Prefer Chrome overrides plus narrowly-scoped per-test thresholds only when the library is visually aligned with Chrome but still differs at the pixel-raster level.
- If a W3C fixture depends on JavaScript, DOM APIs, or browser-only runtime behavior that Svg.Skia does not implement, keep that row skipped with an explicit reason instead of manufacturing a fake baseline.

## Testing Workflow
- For W3C work, first run focused rows with `dotnet test tests/Svg.Skia.UnitTests/Svg.Skia.UnitTests.csproj -f net10.0 -c Release --no-restore --filter "FullyQualifiedName~W3CTestSuiteTests.Tests"`.
- When refreshing baselines, rerun the focused W3C subset that changed before running the full W3C suite.
- Before committing renderer or baseline changes, run:
- `dotnet format Svg.Skia.slnx --no-restore`
- `dotnet build Svg.Skia.slnx -c Release`
- `dotnet test Svg.Skia.slnx -c Release`
- Keep unrelated local state files, especially `samples/TestApp/TestApp.json`, out of commits unless the task explicitly requires them.

## Commit Guidelines
- Use concise commit messages summarizing your change in under 72 characters.
- Additional description lines may follow the summary if necessary.
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Svg.Skia Changelog

## Unreleased

* Added SVG 1.1 animation object-model coverage in `Svg.Custom` for `animate`, `set`, `animateMotion`, `animateColor`, `animateTransform`, and `mpath`.
* Added typed `pointer-events` support, geometry-aware hit testing, topmost-element targeting, and routed interaction dispatch with capture, tunnel, bubble, and cursor resolution.
* Added shared animation playback in `SKSvg`, including animation time control, invalidation events, layered redraw, throttling helpers, and native-composition scene extraction.
* Added host animation backends for Avalonia and Uno, including resolved-backend diagnostics and Avalonia retained `NativeComposition` playback with fallback.
* Added an animation benchmark harness in `tests/Svg.Skia.Benchmarks` and exposed animation/backend controls in `samples/TestApp`.

## 0.3.0

* Updated NuGet packages.
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
<PackageVersion Include="xunit.assert" Version="2.9.3" />
Expand Down
81 changes: 77 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@
## About

*Svg.Skia* can be used as a .NET library or as a CLI application
to render SVG files based on a [static](http://www.w3.org/TR/SVG11/feature#SVG-static)
[SVG Full 1.1](https://www.w3.org/TR/SVG11/) subset to raster images or
to a backend's canvas.
to render SVG files based on the [SVG Full 1.1](https://www.w3.org/TR/SVG11/)
document model to raster images or to a backend's canvas.

The `Svg.Skia` is using [SVG](https://github.com/vvvv/SVG) library to load `Svg` object model.

Expand All @@ -33,6 +32,15 @@ The `Svg.Skia` can be used in same way as the [SkiaSharp.Extended.Svg](https://g
The `Svg` library has a more complete implementation of the `Svg` document model than [SkiaSharp.Extended.Svg](https://github.com/mono/SkiaSharp.Extended/tree/main/source/SkiaSharp.Extended.Svg)
and the `Svg.Skia` renderer will provide more complete rendering subsystem implementation.

## Highlights

- `Svg.Custom` now exposes the SVG 1.1 animation object model for `animate`, `set`, `animateMotion`, `animateColor`, `animateTransform`, and `mpath`.
- `Svg.Custom` and `Svg.Skia` now include typed `pointer-events` handling plus geometry-aware topmost hit testing.
- `Svg.Skia` now includes a shared interaction dispatcher, shared animation clock/controller, and host-driven animation playback APIs.
- `Svg.Controls.Skia.Avalonia` and `Svg.Controls.Skia.Uno` now expose animation backend selection, playback rate, frame interval, and resolved-backend diagnostics.
- Avalonia adds an optional `NativeComposition` animation backend with fallback to `RenderLoop` or `DispatcherTimer` when retained composition is unavailable.
- `tests/Svg.Skia.Benchmarks` adds a local BenchmarkDotNet harness for the shared animation renderer, and `samples/TestApp` exposes backend and playback controls for manual verification.

## NuGet

Svg.Skia is delivered as a NuGet package.
Expand Down Expand Up @@ -202,7 +210,7 @@ The `SKSvg` class provides helpers for retrieving elements or drawables at a
given point. The hit-testing methods expect coordinates in picture space:

```C#
using SkiaSharp;
using ShimSkiaSharp;
using Svg.Skia;

var svg = new SKSvg();
Expand All @@ -220,6 +228,10 @@ When drawing on a transformed canvas you can convert canvas coordinates to
picture coordinates using `TryGetPicturePoint` and then use the hit-testing
methods.

The runtime also exposes `HitTestTopmostElement(...)` for pointer-dispatch
scenarios where only the topmost routed target should be returned, and the hit
test path now respects typed `pointer-events` values.

#### Svg control

The `Svg` Avalonia control exposes a `HitTestElements` method that accepts
Expand All @@ -229,6 +241,47 @@ a point in control coordinates and returns the matching SVG elements:
var hits = svgControl.HitTestElements(new Point(x, y));
```

### Animation and interaction

`SKSvg` now exposes a shared animation runtime and a routed interaction layer
that can be hosted from Avalonia, Uno, or custom Skia surfaces.

```C#
using System;
using ShimSkiaSharp;
using Svg.Skia;

using var svg = new SKSvg();

if (svg.Load("animated.svg") is not null && svg.HasAnimations)
{
svg.AnimationInvalidated += (_, e) => Console.WriteLine(e.Time);

svg.SetAnimationTime(TimeSpan.FromSeconds(1));
svg.AdvanceAnimation(TimeSpan.FromMilliseconds(16));
svg.ResetAnimation();
}

var target = svg.HitTestTopmostElement(new SKPoint(10, 10));
```

The shared runtime surface includes:

- `HasAnimations`
- `AnimationTime`
- `SetAnimationTime(...)`
- `AdvanceAnimation(...)`
- `ResetAnimation()`
- `AnimationInvalidated`
- `AnimationMinimumRenderInterval`
- `HasPendingAnimationFrame`
- `FlushPendingAnimationFrame()`
- `LastAnimationDirtyTargetCount`

The shared interaction surface includes `SvgInteractionDispatcher`,
`SvgPointerInput`, routed `Dispatched` events, cursor hints, and optional
compatibility bridging back into `SvgElement` mouse events.


### Avalonia

Expand All @@ -248,6 +301,26 @@ Install-Package Svg.Controls.Skia.Avalonia
<Svg Path="/Assets/__AJ_Digital_Camera.svg"/>
```

For animated content, the Skia-backed Avalonia and Uno controls also expose:

- `AnimationBackend`
- `AnimationFrameInterval`
- `AnimationPlaybackRate`
- `ActualAnimationBackend`
- `AnimationBackendFallbackReason`

Avalonia supports `Default`, `Manual`, `DispatcherTimer`, `RenderLoop`, and
`NativeComposition`. Uno supports the same host-driven playback model but falls
back from `NativeComposition` because it does not currently expose a working
retained child-visual path.

```XAML
<Svg Path="/Assets/animated.svg"
AnimationBackend="Default"
AnimationPlaybackRate="1.0"
AnimationFrameInterval="0:0:0.016" />
```

#### Image control

```XAML
Expand Down
5 changes: 5 additions & 0 deletions Svg.Skia.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@
<Project Path="samples/Svg.SourceGenerator.Skia.Sample/Svg.SourceGenerator.Skia.Sample.csproj" />
<Project Path="samples/svgc/svgc.csproj" />
<Project Path="samples/SvgToPng/SvgToPng.csproj" />
<Project Path="samples/TestApp.Shared/TestApp.Shared.csproj" />
<Project Path="samples/TestApp/TestApp.csproj" />
<Project Path="samples/UnoTestApp/UnoTestApp.csproj" />
</Folder>
<Folder Name="/src/">
<Project Path="src/ShimSkiaSharp/ShimSkiaSharp.csproj" />
Expand All @@ -81,11 +83,14 @@
<Project Path="src/Svg.Controls.Skia.Uno/Svg.Controls.Skia.Uno.csproj" />
<Project Path="src/Svg.Controls.Skia.Avalonia/Svg.Controls.Skia.Avalonia.csproj" />
<Project Path="src/Svg.Model/Svg.Model.csproj" />
<Project Path="src/Svg.Animation/Svg.Animation.csproj" />
<Project Path="src/Svg.SceneGraph/Svg.SceneGraph.csproj" />
<Project Path="src/Svg.Skia/Svg.Skia.csproj" />
<Project Path="src/Svg.SourceGenerator.Skia/Svg.SourceGenerator.Skia.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/ShimSkiaSharp.UnitTests/ShimSkiaSharp.UnitTests.csproj" />
<Project Path="tests/Svg.Skia.Benchmarks/Svg.Skia.Benchmarks.csproj" />
<Project Path="tests/Svg.Controls.Avalonia.UnitTests/Svg.Controls.Avalonia.UnitTests.csproj" />
<Project Path="tests/Svg.Controls.Skia.Avalonia.UnitTests/Svg.Controls.Skia.Avalonia.UnitTests.csproj" />
<Project Path="tests/Svg.Controls.Skia.Uno.UnitTests/Svg.Controls.Skia.Uno.UnitTests.csproj" />
Expand Down
76 changes: 76 additions & 0 deletions plan/animation-project-split-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Animation Project Split Plan

## Goal

Split the reusable animation runtime out of `src/Svg.Skia` into a dedicated project, similar to `src/Svg.SceneGraph`, while keeping `SKSvg`-specific render orchestration inside `Svg.Skia`.

## Why

- reduce `Svg.Skia` assembly scope to Skia/render-host integration
- make the animation runtime reusable by controls and future backends without pulling in all of `SKSvg`
- isolate trim/AOT-sensitive animation logic behind a smaller assembly boundary
- align the architecture with the retained-scene split already introduced by `Svg.SceneGraph`

## Clean Boundary

### Move to `src/Svg.Animation`

- `SvgAnimationClock`
- `SvgAnimationController`
- `SvgAnimationFrameState`
- `SvgAnimationHostBackend` and resolver/capability types
- `SvgNativeCompositionScene`, `SvgNativeCompositionFrame`, `SvgNativeCompositionLayer`
- shared animation invalidation policy extracted from `SKSvg.AnimationLayers.cs`

### Keep in `src/Svg.Skia`

- `SKSvg.Model.cs` animation orchestration
- `SKSvg.AnimationLayers.cs`
- `SKSvg.NativeComposition.cs`
- `SKSvg.SceneGraph.cs` retained-scene mutation/render preparation
- SkiaSharp picture conversion, picture registration/disposal, and drawing

Reason: those files are partial `SKSvg` implementation details and depend directly on `SKSvg` state, `SkiaModel`, and render lifecycle synchronization.

## Dependency Rules

### `Svg.Animation`

- may reference:
- `Svg.Custom`
- `Svg.Model`
- `ShimSkiaSharp`
- `SkiaSharp`
- must not depend on:
- `SKSvg`
- `Svg.Controls.*`
- retained-scene orchestration partials

### `Svg.Skia`

- references:
- `Svg.Animation`
- `Svg.SceneGraph`
- existing model/custom projects

## Required Supporting Changes

1. Add `InternalsVisibleTo` from `Svg.Custom` to `Svg.Animation`.
2. Add `src/Svg.Animation/Properties/AssemblyInfo.cs` with `InternalsVisibleTo` for `Svg.Skia`.
3. Add the new project to `Svg.Skia.slnx`.
4. Update `Svg.Skia.csproj` to reference `Svg.Animation.csproj`.
5. Physically move animation runtime files from `src/Svg.Skia/Animation` to `src/Svg.Animation`.
6. Replace the inherited-animation-attribute table in `SKSvg.AnimationLayers.cs` with a shared helper owned by `Svg.Animation`.

## Validation

1. `dotnet format Svg.Skia.slnx --no-restore`
2. `dotnet build Svg.Skia.slnx -c Release`
3. `dotnet test Svg.Skia.slnx -c Release`

## Success Criteria

- `src/Svg.Skia/Animation` no longer contains the reusable runtime files
- `Svg.Skia` builds against the new project boundary
- Avalonia/Uno host backends and tests continue to compile against the moved types
- `SKSvg.AnimationLayers.cs` retains only `SKSvg`-bound logic
Loading
Loading