Skip to content

Cloning improvements#459

Merged
wieslawsoltes merged 9 commits into
masterfrom
CloningImprovments
Jan 5, 2026
Merged

Cloning improvements#459
wieslawsoltes merged 9 commits into
masterfrom
CloningImprovments

Conversation

@wieslawsoltes
Copy link
Copy Markdown
Owner

Overview

This change set focuses on safe cloning and reload of SvgSource and SvgImage, plus safer render and dispose behavior for SKSvg based drawing. Samples and tests are updated to demonstrate and validate clone usage and reload paths.

Key Changes

Svg.Controls.Skia.Avalonia

  • SvgSource now supports cloning with independent model data and reload metadata (path, stream, base URI, parameters). The clone path prefers SKSvg.Clone when possible and falls back to SKPicture cloning when there is no reload source.
  • Loading and reload now preserve original data for all entry points (path, stream, SVG string, SvgDocument) by caching in memory and reusing cached streams when reloading.
  • Picture is lazy and derived from SKSvg when available; path based load is deferred and uses the cached parameters and entities.
  • Dispose now guards against render time disposal by tracking active renders and deferring cleanup until rendering completes.
  • Added render guards (BeginRender/EndRender) used by the custom draw operation to avoid races with dispose.

Svg.Skia (core)

  • SKSvg.Picture is now lazy and thread safe; it builds a picture from the model outside the lock and installs it only if the model is still current.
  • Draw now tracks active draws; reset and rebuild operations wait for active draws to complete before disposing pictures.
  • OnDraw now fires after draw completion to avoid holding draw state while handlers run.
  • RebuildFromModel and Reset now dispose pictures safely even when draw operations are in flight.
  • ReLoad captures reload metadata, resets state, and reloads outside the lock.
  • Internal type fix: SKSvg.Picture uses SkiaSharp.SKPicture for the cached picture field to avoid ShimSkiaSharp conversion errors.

Svg.Controls.Avalonia (non Skia)

  • SvgSource gains RebuildFromModel and Clone for deep cloning SKPicture state.
  • SvgImage gains Clone for deep cloning its source.

Samples

  • Model sample tabs now label the right side as "Clone (Grayscale)" and update code behind to use SvgImage.Clone.

Tests

  • Added clone coverage for SvgSource and SvgImage in both Avalonia and Avalonia Skia tests.
  • Added reload coverage for SvgSource (Skia).
  • Added a regression test to ensure disposing during render does not deadlock (Skia SvgSource).

API Changes

Avalonia.Svg

  • SvgSource.Clone() added.
  • SvgSource.RebuildFromModel() added.
  • SvgImage.Clone() added.

Avalonia.Svg.Skia

  • SvgSource.Clone() added.
  • SvgImage.Clone() added and preserves Css and CurrentCss.
  • SvgSource.ReLoad behavior now supports all creation paths (path, stream, SVG string, SvgDocument) and clones by caching the original data.

Svg.Skia

  • SKSvg.Picture now lazy and thread safe.
  • SKSvg draws and rebuilds are coordinated via active draw tracking (behavior change, no new public surface).

Usage Examples

Clone a Skia SvgSource and edit the model

var source = Avalonia.Svg.Skia.SvgSource.LoadFromSvg(svgText);
var clone = source.Clone();

// Update model commands on the clone, then rebuild.
if (clone.Svg?.Model is { } model)
{
    // mutate model commands
}

clone.RebuildFromModel();

Reload from a modified model without keeping original sources

var source = Avalonia.Svg.Skia.SvgSource.LoadFromSvg(svgText);

if (source.Svg?.Model is { } model)
{
    // mutate model commands
}

// Rebuild from the modified model instead of reloading from path/stream.
source.RebuildFromModel();

Clone a Skia SvgImage and preserve CSS

var image = new Avalonia.Svg.Skia.SvgImage
{
    Source = Avalonia.Svg.Skia.SvgSource.Load("Assets/icon.svg", baseUri: null),
    Css = ".primary { fill: #ff0000; }",
    CurrentCss = ".accent { fill: #0000ff; }"
};

var clone = image.Clone();

Reload a Skia SvgSource with new CSS

var source = Avalonia.Svg.Skia.SvgSource.LoadFromSvg(svgText);
source.ReLoad(new SvgParameters(source.Parameters?.Entities, ".Black { fill: #000; }"));

Clone, modify, then reload from cached original source

var source = Avalonia.Svg.Skia.SvgSource.Load("Assets/icon.svg", baseUri: null);
var clone = source.Clone();

// Make temporary model changes on the clone.
if (clone.Svg?.Model is { } model)
{
    // mutate model commands
}
clone.RebuildFromModel();

// Later, revert clone back to the original SVG by reloading from its cached source.
clone.ReLoad(clone.Parameters);

Clone a non Skia SvgSource and rebuild

var source = Avalonia.Svg.SvgSource.LoadFromSvg(svgText);
var clone = source.Clone();
clone.RebuildFromModel();

Validation

  • dotnet build Svg.Skia.slnx -c Release (warnings only)

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.

1 participant