One of the more useful parts of Svg.Skia is that it does not stop at "load and draw". The runtime keeps several layers alive, and each layer has a different rebuild story.
After loading, SKSvg can expose:
SourceDocument for authored SVG DOM work,RetainedSceneGraph for compiled-node inspection and incremental mutation refresh,Model for low-level ShimSkiaSharp.SKPicture command editing,Picture for the current native SkiaSharp.SKPicture.Choose the layer that matches the kind of change you are making.
When the DOM changes structurally or when the simplest path is good enough, mutate the document and call FromSvgDocument(...) again:
using System.Drawing;
using Svg;
using Svg.Skia;
using var svg = new SKSvg();
svg.FromSvg("<svg width=\"10\" height=\"10\"><rect id=\"r\" width=\"10\" height=\"10\" fill=\"red\" /></svg>");
var rect = (SvgRectangle)svg.SourceDocument!.GetElementById("r")!;
rect.Fill = new SvgColourServer(Color.BlueViolet);
svg.FromSvgDocument(svg.SourceDocument);
This recompiles the retained scene and regenerates the current Picture.
For small localized DOM edits, update the scene-backed document and ask the retained scene to recompile only the affected compilation roots:
using System.Drawing;
using Svg;
using Svg.Skia;
using var svg = new SKSvg();
svg.FromSvg("<svg width=\"10\" height=\"10\"><rect id=\"r\" width=\"10\" height=\"10\" fill=\"red\" /></svg>");
var scene = svg.RetainedSceneGraph!;
var rect = (SvgRectangle)scene.SourceDocument!.GetElementById("r")!;
rect.Fill = new SvgColourServer(Color.BlueViolet);
svg.TryApplyRetainedSceneMutationByIdAndRender("r", new[] { "fill" }, out var result);
This path keeps the current Picture refreshed without paying for a full rebuild every time.
SKSvg.Model exposes the ShimSkiaSharp.SKPicture command tree. This is the structure that SkiaModel later turns into SkiaSharp.SKPicture.
Because the model is a plain command graph, you can inspect and mutate it before rebuilding the final picture:
using System.Linq;
using ShimSkiaSharp;
using Svg.Skia;
var svg = new SKSvg();
svg.FromSvg("<svg width=\"10\" height=\"10\"><rect width=\"10\" height=\"10\" fill=\"red\" /></svg>");
foreach (var cmd in svg.Model?.Commands?.OfType<DrawPathCanvasCommand>() ?? Enumerable.Empty<DrawPathCanvasCommand>())
{
if (cmd.Paint?.Color is { } color)
{
cmd.Paint.Color = new SKColor(color.Red, color.Red, color.Red, color.Alpha);
}
}
svg.RebuildFromModel();
The same ideas exist in the Avalonia wrappers:
Avalonia.Svg.Skia.SvgSource.RebuildFromModel()Avalonia.Svg.Skia.SvgSource.ReLoad(...)Avalonia.Svg.Skia.SvgSource.LoadFromSvgDocument(...)Avalonia.Svg.SvgSource.RebuildFromModel()This makes it possible to keep an SVG-backed image source in XAML while still choosing between DOM rebuilds, parameter reloads, and low-level model mutation from code.
The Avalonia image and source types provide cloning helpers so you can keep one original asset and derive modified variants without mutating shared state:
SvgSource.Clone()SvgImage.Clone()Use FromSvgDocument(...) when:
Use retained-scene mutation refresh when:
Use Model plus RebuildFromModel() when: