Browse Source

ImmediateContext + ServerCustomCompositionVisual + bugfixes

Nikita Tsukanov 2 years ago
parent
commit
3fdd1da090
32 changed files with 967 additions and 221 deletions
  1. 51 38
      samples/ControlCatalog/Pages/CompositionPage.axaml
  2. 172 0
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  3. 1 4
      src/Avalonia.Base/Media/Brush.cs
  4. 2 2
      src/Avalonia.Base/Media/BrushExtensions.cs
  5. 141 141
      src/Avalonia.Base/Media/Brushes.cs
  6. 1 1
      src/Avalonia.Base/Media/ConicGradientBrush.cs
  7. 9 0
      src/Avalonia.Base/Media/IImmutableBrush.cs
  8. 2 2
      src/Avalonia.Base/Media/IMutableBrush.cs
  9. 9 0
      src/Avalonia.Base/Media/ISolidColorBrush.cs
  10. 2 2
      src/Avalonia.Base/Media/ImageBrush.cs
  11. 373 0
      src/Avalonia.Base/Media/ImmediateDrawingContext.cs
  12. 1 1
      src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs
  13. 1 3
      src/Avalonia.Base/Media/Immutable/ImmutablePen.cs
  14. 1 1
      src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs
  15. 1 1
      src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
  16. 3 3
      src/Avalonia.Base/Media/KnownColors.cs
  17. 1 1
      src/Avalonia.Base/Media/LinearGradientBrush.cs
  18. 1 1
      src/Avalonia.Base/Media/RadialGradientBrush.cs
  19. 2 2
      src/Avalonia.Base/Media/SolidColorBrush.cs
  20. 2 2
      src/Avalonia.Base/Media/VisualBrush.cs
  21. 2 0
      src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
  22. 1 1
      src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs
  23. 37 0
      src/Avalonia.Base/Rendering/Composition/CompositionCustomVisual.cs
  24. 36 0
      src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs
  25. 2 0
      src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
  26. 6 0
      src/Avalonia.Base/Rendering/Composition/Server/IServerClockItem.cs
  27. 0 2
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  28. 16 0
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  29. 13 12
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
  30. 74 0
      src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs
  31. 1 1
      src/Avalonia.Base/Rendering/Composition/VisualCollection.cs
  32. 3 0
      src/Avalonia.Base/Visual.cs

+ 51 - 38
samples/ControlCatalog/Pages/CompositionPage.axaml

@@ -2,44 +2,57 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:pages="using:ControlCatalog.Pages"
              x:Class="ControlCatalog.Pages.CompositionPage">
-    <StackPanel>
-        <TextBlock Classes="h1">Implicit animations</TextBlock>
+  <TabControl>
+    <TabItem Header="Implicit animations">
+      <StackPanel>
         <Grid ColumnDefinitions="*,10,40" Margin="0 0 40 0">
-            <ItemsControl x:Name="Items">
-                <ItemsControl.ItemsPanel>
-                    <ItemsPanelTemplate>
-                        <WrapPanel/>
-                    </ItemsPanelTemplate>
-                </ItemsControl.ItemsPanel>
-                <ItemsControl.DataTemplates>
-                    <DataTemplate DataType="pages:CompositionPageColorItem">
-                        <Border 
-                            pages:CompositionPage.EnableAnimations="True"
-                            Padding="10" BorderBrush="Gray" BorderThickness="2"
-                            Background="{Binding ColorBrush}" Width="100" Height="100" Margin="10">
-                            <TextBlock Text="{Binding ColorHexValue}"/>
-                        </Border>
-                    </DataTemplate>
-                </ItemsControl.DataTemplates>
-            </ItemsControl>
-            <GridSplitter Margin="2" BorderThickness="1" BorderBrush="Gray" 
-                          Background="#e0e0e0" Grid.Column="1"
-                          ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"
-            />
-            <Border Grid.Column="2">
-                <LayoutTransformControl
-                    HorizontalAlignment="Center"
-            
-                    MinWidth="30">
-                    <LayoutTransformControl.LayoutTransform>
-                        <RotateTransform Angle="90"/>
-                    </LayoutTransformControl.LayoutTransform>
-            
-                    <TextBlock>Resize me</TextBlock>
-                </LayoutTransformControl>
-            </Border>
+          <ItemsControl x:Name="Items">
+            <ItemsControl.ItemsPanel>
+              <ItemsPanelTemplate>
+                <WrapPanel/>
+              </ItemsPanelTemplate>
+            </ItemsControl.ItemsPanel>
+            <ItemsControl.DataTemplates>
+              <DataTemplate DataType="pages:CompositionPageColorItem">
+                <Border 
+                  pages:CompositionPage.EnableAnimations="True"
+                  Padding="10" BorderBrush="Gray" BorderThickness="2"
+                  Background="{Binding ColorBrush}" Width="100" Height="100" Margin="10">
+                    <TextBlock Text="{Binding ColorHexValue}"/>
+                </Border>
+              </DataTemplate>
+            </ItemsControl.DataTemplates>
+          </ItemsControl>
+          <GridSplitter Margin="2" BorderThickness="1" BorderBrush="Gray" 
+                        Background="#e0e0e0" Grid.Column="1"
+                        ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"
+          />
+          <Border Grid.Column="2">
+            <LayoutTransformControl HorizontalAlignment="Center" MinWidth="30">
+              <LayoutTransformControl.LayoutTransform>
+                <RotateTransform Angle="90"/>
+              </LayoutTransformControl.LayoutTransform>
+              <TextBlock>Resize me</TextBlock>
+            </LayoutTransformControl>
+          </Border>
         </Grid>
-
-     
-    </StackPanel>
+      </StackPanel>
+    </TabItem>
+    <TabItem Header="Animation">
+      <DockPanel>
+        <Button DockPanel.Dock="Top" Margin="10" Click="ButtonThreadSleep">Thread.Sleep(10000);</Button>
+        <Control x:Name="SolidVisualHost" />
+      </DockPanel>
+    </TabItem>
+    <TabItem Header="Custom">
+      <DockPanel>
+        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
+          <Button Margin="10" Click="ButtonThreadSleep">Thread.Sleep(10000);</Button>
+          <Button Margin="10" Click="ButtonStartCustomVisual">Start</Button>
+          <Button Margin="10" Click="ButtonStopCustomVisual">Stop</Button>
+        </StackPanel>
+        <Control x:Name="CustomVisualHost" />
+      </DockPanel>
+    </TabItem>
+  </TabControl>
 </UserControl>

+ 172 - 0
samples/ControlCatalog/Pages/CompositionPage.axaml.cs

@@ -1,28 +1,39 @@
 using System;
 using System.Collections.Generic;
+using System.Numerics;
+using System.Threading;
 using Avalonia;
+using Avalonia.Animation;
 using Avalonia.Controls;
+using Avalonia.Interactivity;
 using Avalonia.Markup.Xaml;
 using Avalonia.Media;
+using Avalonia.Media.Immutable;
 using Avalonia.Rendering.Composition;
 using Avalonia.Rendering.Composition.Animations;
 using Avalonia.VisualTree;
+using Math = System.Math;
 
 namespace ControlCatalog.Pages;
 
 public partial class CompositionPage : UserControl
 {
     private ImplicitAnimationCollection? _implicitAnimations;
+    private CompositionCustomVisual? _customVisual;
+    private CompositionSolidColorVisual? _solidVisual;
 
     public CompositionPage()
     {
         AvaloniaXamlLoader.Load(this);
+        AttachAnimatedSolidVisual(this.FindControl<Control>("SolidVisualHost")!);
+        AttachCustomVisual(this.FindControl<Control>("CustomVisualHost")!);
     }
 
     protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
     {
         base.OnAttachedToVisualTree(e);
         this.Get<ItemsControl>("Items").Items = CreateColorItems();
+
     }
 
     private static List<CompositionPageColorItem> CreateColorItems()
@@ -126,6 +137,167 @@ public partial class CompositionPage : UserControl
             compositionVisual.ImplicitAnimations = page._implicitAnimations;
         }
     }
+    
+    void AttachAnimatedSolidVisual(Visual v)
+    {
+        void Update()
+        {
+            if(_solidVisual == null)
+                return;
+            _solidVisual.Size = new Vector2((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3);
+            _solidVisual.Offset = new Vector3((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3, 0);
+        }
+        v.AttachedToVisualTree += delegate
+        {
+            var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
+            if(compositor == null || _solidVisual?.Compositor == compositor)
+                return;
+            _solidVisual = compositor.CreateSolidColorVisual();
+            ElementComposition.SetElementChildVisual(v, _solidVisual);
+            _solidVisual.Color = Colors.Red;
+            var animation = _solidVisual.Compositor.CreateColorKeyFrameAnimation();
+            animation.InsertKeyFrame(0, Colors.Red);
+            animation.InsertKeyFrame(0.5f, Colors.Blue);
+            animation.InsertKeyFrame(1, Colors.Green);
+            animation.Duration = TimeSpan.FromSeconds(5);
+            animation.IterationBehavior = AnimationIterationBehavior.Forever;
+            animation.Direction = PlaybackDirection.Alternate;
+            _solidVisual.StartAnimation("Color", animation);
+
+            _solidVisual.AnchorPoint = new Vector2(0, 0);
+
+            var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation();
+            scale.Duration = TimeSpan.FromSeconds(5);
+            scale.IterationBehavior = AnimationIterationBehavior.Forever;
+            scale.InsertKeyFrame(0, new Vector3(1, 1, 0));
+            scale.InsertKeyFrame(0.5f, new Vector3(1.5f, 1.5f, 0));
+            scale.InsertKeyFrame(1, new Vector3(1, 1, 0));
+
+            _solidVisual.StartAnimation("Scale", scale);
+
+            var center =
+                _solidVisual.Compositor.CreateExpressionAnimation(
+                    "Vector3(this.Target.Size.X * 0.5, this.Target.Size.Y * 0.5, 1)");
+            _solidVisual.StartAnimation("CenterPoint", center);
+            Update();
+        };
+        v.PropertyChanged += (_, a) =>
+        {
+            if (a.Property == BoundsProperty)
+                Update();
+        };
+    }
+
+    void AttachCustomVisual(Visual v)
+    {
+        void Update()
+        {
+            if (_customVisual == null)
+                return;
+            var h = (float)Math.Min(v.Bounds.Height, v.Bounds.Width / 3);
+            _customVisual.Size = new Vector2((float)v.Bounds.Width, h);
+            _customVisual.Offset = new Vector3(0, (float)(v.Bounds.Height - h) / 2, 0);
+        }
+        v.AttachedToVisualTree += delegate
+        {
+            var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
+            if(compositor == null || _customVisual?.Compositor == compositor)
+                return;
+            _customVisual = compositor.CreateCustomVisual(new CustomVisualHandler());
+            ElementComposition.SetElementChildVisual(v, _customVisual);
+            _customVisual.SendHandlerMessage(CustomVisualHandler.StartMessage);
+            Update();
+        };
+        
+        v.PropertyChanged += (_, a) =>
+        {
+            if (a.Property == BoundsProperty)
+                Update();
+        };
+    }
+
+    class CustomVisualHandler : CompositionCustomVisualHandler
+    {
+        private TimeSpan _animationElapsed;
+        private TimeSpan? _lastServerTime;
+        private bool _running;
+
+        public static readonly object StopMessage = new(), StartMessage = new();
+        
+        public override void OnRender(ImmediateDrawingContext drawingContext)
+        {
+            if (_running)
+            {
+                if (_lastServerTime.HasValue) _animationElapsed += (CompositionNow - _lastServerTime.Value);
+                _lastServerTime = CompositionNow;
+            }
+            
+            const int cnt = 20;
+            var maxPointSizeX = EffectiveSize.X / (cnt * 1.6);
+            var maxPointSizeY = EffectiveSize.Y / 4;
+            var pointSize = Math.Min(maxPointSizeX, maxPointSizeY);
+            var animationLength = TimeSpan.FromSeconds(4);
+            var animationStage = _animationElapsed.TotalSeconds / animationLength.TotalSeconds;
+
+            var sinOffset = Math.Cos(_animationElapsed.TotalSeconds) * 1.5;
+            
+            for (var c = 0; c < cnt; c++)
+            {
+                var stage = (animationStage + (double)c / cnt) % 1;
+                var colorStage =
+                    (animationStage + (Math.Sin(_animationElapsed.TotalSeconds * 2) + 1) / 2 + (double)c / cnt) % 1;
+                var posX = (EffectiveSize.X + pointSize * 3) * stage - pointSize;
+                var posY = (EffectiveSize.Y - pointSize) * (1 + Math.Sin(stage * 3.14 * 3 + sinOffset)) / 2 + pointSize / 2;
+                var opacity = Math.Sin(stage * 3.14);
+
+
+                drawingContext.DrawEllipse(new ImmutableSolidColorBrush(Color.FromArgb(
+                        255, 
+                        (byte)(255 - 255 * colorStage),
+                        (byte)(255 * Math.Abs(0.5 - colorStage) * 2), 
+                        (byte)(255 * colorStage)
+                    ), opacity), null,
+                    new Point(posX, posY), pointSize / 2, pointSize / 2);
+            }
+            
+        }
+
+        public override void OnMessage(object message)
+        {
+            if (message == StartMessage)
+            {
+                _running = true;
+                _lastServerTime = null;
+                RegisterForNextAnimationFrameUpdate();
+            }
+            else if (message == StopMessage)
+                _running = false;
+        }
+
+        public override void OnAnimationFrameUpdate()
+        {
+            if (_running)
+            {
+                Invalidate();
+                RegisterForNextAnimationFrameUpdate();
+            }
+        }
+    }
+    
+    private void ButtonThreadSleep(object? sender, RoutedEventArgs e)
+    {
+        Thread.Sleep(10000);
+    }
+
+    private void ButtonStartCustomVisual(object? sender, RoutedEventArgs e)
+    {
+        _customVisual?.SendHandlerMessage(CustomVisualHandler.StartMessage);
+    }
+
+    private void ButtonStopCustomVisual(object? sender, RoutedEventArgs e)
+    {
+        _customVisual?.SendHandlerMessage(CustomVisualHandler.StopMessage);
+    }
 }
 
 public class CompositionPageColorItem

+ 1 - 4
src/Avalonia.Base/Media/Brush.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Media
     /// Describes how an area is painted.
     /// </summary>
     [TypeConverter(typeof(BrushConverter))]
-    public abstract class Brush : Animatable, IMutableBrush
+    public abstract class Brush : Animatable
     {
         /// <summary>
         /// Defines the <see cref="Opacity"/> property.
@@ -92,9 +92,6 @@ namespace Avalonia.Media
             throw new FormatException($"Invalid brush string: '{s}'.");
         }
 
-        /// <inheritdoc/>
-        public abstract IBrush ToImmutable();
-
         /// <summary>
         /// Marks a property as affecting the brush's visual representation.
         /// </summary>

+ 2 - 2
src/Avalonia.Base/Media/BrushExtensions.cs

@@ -16,11 +16,11 @@ namespace Avalonia.Media
         /// The result of calling <see cref="IMutableBrush.ToImmutable"/> if the brush is mutable,
         /// otherwise <paramref name="brush"/>.
         /// </returns>
-        public static IBrush ToImmutable(this IBrush brush)
+        public static IImmutableBrush ToImmutable(this IBrush brush)
         {
             _ = brush ?? throw new ArgumentNullException(nameof(brush));
 
-            return (brush as IMutableBrush)?.ToImmutable() ?? brush;
+            return (brush as IMutableBrush)?.ToImmutable() ?? (IImmutableBrush)brush;
         }
 
         /// <summary>

+ 141 - 141
src/Avalonia.Base/Media/Brushes.cs

@@ -8,706 +8,706 @@ namespace Avalonia.Media
         /// <summary>
         /// Gets an <see cref="Colors.AliceBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush();
+        public static IImmutableSolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.AntiqueWhite"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush();
+        public static IImmutableSolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Aqua"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Aqua => KnownColor.Aqua.ToBrush();
+        public static IImmutableSolidColorBrush Aqua => KnownColor.Aqua.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Aquamarine"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush();
+        public static IImmutableSolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Azure"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Azure => KnownColor.Azure.ToBrush();
+        public static IImmutableSolidColorBrush Azure => KnownColor.Azure.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Beige"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Beige => KnownColor.Beige.ToBrush();
+        public static IImmutableSolidColorBrush Beige => KnownColor.Beige.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Bisque"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Bisque => KnownColor.Bisque.ToBrush();
+        public static IImmutableSolidColorBrush Bisque => KnownColor.Bisque.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Black"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Black => KnownColor.Black.ToBrush();
+        public static IImmutableSolidColorBrush Black => KnownColor.Black.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.BlanchedAlmond"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush();
+        public static IImmutableSolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Blue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Blue => KnownColor.Blue.ToBrush();
+        public static IImmutableSolidColorBrush Blue => KnownColor.Blue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.BlueViolet"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush();
+        public static IImmutableSolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Brown"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Brown => KnownColor.Brown.ToBrush();
+        public static IImmutableSolidColorBrush Brown => KnownColor.Brown.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.BurlyWood"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush();
+        public static IImmutableSolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.CadetBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush();
+        public static IImmutableSolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Chartreuse"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush();
+        public static IImmutableSolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Chocolate"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush();
+        public static IImmutableSolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Coral"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Coral => KnownColor.Coral.ToBrush();
+        public static IImmutableSolidColorBrush Coral => KnownColor.Coral.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.CornflowerBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush();
+        public static IImmutableSolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Cornsilk"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush();
+        public static IImmutableSolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Crimson"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Crimson => KnownColor.Crimson.ToBrush();
+        public static IImmutableSolidColorBrush Crimson => KnownColor.Crimson.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Cyan"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Cyan => KnownColor.Cyan.ToBrush();
+        public static IImmutableSolidColorBrush Cyan => KnownColor.Cyan.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush();
+        public static IImmutableSolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkCyan"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush();
+        public static IImmutableSolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkGoldenrod"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush();
+        public static IImmutableSolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkGray"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush();
+        public static IImmutableSolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush();
+        public static IImmutableSolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkKhaki"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush();
+        public static IImmutableSolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkMagenta"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush();
+        public static IImmutableSolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkOliveGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush();
+        public static IImmutableSolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkOrange"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush();
+        public static IImmutableSolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkOrchid"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush();
+        public static IImmutableSolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkRed"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush();
+        public static IImmutableSolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkSalmon"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush();
+        public static IImmutableSolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkSeaGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush();
+        public static IImmutableSolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkSlateBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush();
+        public static IImmutableSolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkSlateGray"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush();
+        public static IImmutableSolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkTurquoise"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush();
+        public static IImmutableSolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DarkViolet"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush();
+        public static IImmutableSolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DeepPink"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush();
+        public static IImmutableSolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DeepSkyBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush();
+        public static IImmutableSolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DimGray"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DimGray => KnownColor.DimGray.ToBrush();
+        public static IImmutableSolidColorBrush DimGray => KnownColor.DimGray.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.DodgerBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush();
+        public static IImmutableSolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Firebrick"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush();
+        public static IImmutableSolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.FloralWhite"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush();
+        public static IImmutableSolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.ForestGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush();
+        public static IImmutableSolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Fuchsia"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush();
+        public static IImmutableSolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Gainsboro"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush();
+        public static IImmutableSolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.GhostWhite"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush();
+        public static IImmutableSolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Gold"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Gold => KnownColor.Gold.ToBrush();
+        public static IImmutableSolidColorBrush Gold => KnownColor.Gold.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Goldenrod"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush();
+        public static IImmutableSolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Gray"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Gray => KnownColor.Gray.ToBrush();
+        public static IImmutableSolidColorBrush Gray => KnownColor.Gray.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Green"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Green => KnownColor.Green.ToBrush();
+        public static IImmutableSolidColorBrush Green => KnownColor.Green.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.GreenYellow"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush();
+        public static IImmutableSolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Honeydew"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush();
+        public static IImmutableSolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.HotPink"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush HotPink => KnownColor.HotPink.ToBrush();
+        public static IImmutableSolidColorBrush HotPink => KnownColor.HotPink.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.IndianRed"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush();
+        public static IImmutableSolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Indigo"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Indigo => KnownColor.Indigo.ToBrush();
+        public static IImmutableSolidColorBrush Indigo => KnownColor.Indigo.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Ivory"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Ivory => KnownColor.Ivory.ToBrush();
+        public static IImmutableSolidColorBrush Ivory => KnownColor.Ivory.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Khaki"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Khaki => KnownColor.Khaki.ToBrush();
+        public static IImmutableSolidColorBrush Khaki => KnownColor.Khaki.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Lavender"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Lavender => KnownColor.Lavender.ToBrush();
+        public static IImmutableSolidColorBrush Lavender => KnownColor.Lavender.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LavenderBlush"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush();
+        public static IImmutableSolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LawnGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush();
+        public static IImmutableSolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LemonChiffon"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush();
+        public static IImmutableSolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush();
+        public static IImmutableSolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightCoral"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush();
+        public static IImmutableSolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightCyan"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush();
+        public static IImmutableSolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightGoldenrodYellow"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush();
+        public static IImmutableSolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightGray"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightGray => KnownColor.LightGray.ToBrush();
+        public static IImmutableSolidColorBrush LightGray => KnownColor.LightGray.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush();
+        public static IImmutableSolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightPink"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightPink => KnownColor.LightPink.ToBrush();
+        public static IImmutableSolidColorBrush LightPink => KnownColor.LightPink.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightSalmon"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush();
+        public static IImmutableSolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightSeaGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush();
+        public static IImmutableSolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightSkyBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush();
+        public static IImmutableSolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightSlateGray"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush();
+        public static IImmutableSolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightSteelBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush();
+        public static IImmutableSolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LightYellow"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush();
+        public static IImmutableSolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Lime"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Lime => KnownColor.Lime.ToBrush();
+        public static IImmutableSolidColorBrush Lime => KnownColor.Lime.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.LimeGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush();
+        public static IImmutableSolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Linen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Linen => KnownColor.Linen.ToBrush();
+        public static IImmutableSolidColorBrush Linen => KnownColor.Linen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Magenta"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Magenta => KnownColor.Magenta.ToBrush();
+        public static IImmutableSolidColorBrush Magenta => KnownColor.Magenta.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Maroon"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Maroon => KnownColor.Maroon.ToBrush();
+        public static IImmutableSolidColorBrush Maroon => KnownColor.Maroon.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumAquamarine"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush();
+        public static IImmutableSolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush();
+        public static IImmutableSolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumOrchid"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush();
+        public static IImmutableSolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumPurple"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush();
+        public static IImmutableSolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumSeaGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush();
+        public static IImmutableSolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumSlateBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush();
+        public static IImmutableSolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumSpringGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush();
+        public static IImmutableSolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumTurquoise"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush();
+        public static IImmutableSolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MediumVioletRed"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush();
+        public static IImmutableSolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MidnightBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush();
+        public static IImmutableSolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MintCream"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MintCream => KnownColor.MintCream.ToBrush();
+        public static IImmutableSolidColorBrush MintCream => KnownColor.MintCream.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.MistyRose"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush();
+        public static IImmutableSolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Moccasin"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush();
+        public static IImmutableSolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.NavajoWhite"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush();
+        public static IImmutableSolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Navy"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Navy => KnownColor.Navy.ToBrush();
+        public static IImmutableSolidColorBrush Navy => KnownColor.Navy.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.OldLace"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush OldLace => KnownColor.OldLace.ToBrush();
+        public static IImmutableSolidColorBrush OldLace => KnownColor.OldLace.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Olive"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Olive => KnownColor.Olive.ToBrush();
+        public static IImmutableSolidColorBrush Olive => KnownColor.Olive.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.OliveDrab"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush();
+        public static IImmutableSolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Orange"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Orange => KnownColor.Orange.ToBrush();
+        public static IImmutableSolidColorBrush Orange => KnownColor.Orange.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.OrangeRed"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush();
+        public static IImmutableSolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Orchid"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Orchid => KnownColor.Orchid.ToBrush();
+        public static IImmutableSolidColorBrush Orchid => KnownColor.Orchid.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.PaleGoldenrod"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush();
+        public static IImmutableSolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.PaleGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush();
+        public static IImmutableSolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.PaleTurquoise"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush();
+        public static IImmutableSolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.PaleVioletRed"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush();
+        public static IImmutableSolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.PapayaWhip"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush();
+        public static IImmutableSolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.PeachPuff"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush();
+        public static IImmutableSolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Peru"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Peru => KnownColor.Peru.ToBrush();
+        public static IImmutableSolidColorBrush Peru => KnownColor.Peru.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Pink"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Pink => KnownColor.Pink.ToBrush();
+        public static IImmutableSolidColorBrush Pink => KnownColor.Pink.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Plum"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Plum => KnownColor.Plum.ToBrush();
+        public static IImmutableSolidColorBrush Plum => KnownColor.Plum.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.PowderBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush();
+        public static IImmutableSolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Purple"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Purple => KnownColor.Purple.ToBrush();
+        public static IImmutableSolidColorBrush Purple => KnownColor.Purple.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Red"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Red => KnownColor.Red.ToBrush();
+        public static IImmutableSolidColorBrush Red => KnownColor.Red.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.RosyBrown"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush();
+        public static IImmutableSolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.RoyalBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush();
+        public static IImmutableSolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SaddleBrown"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush();
+        public static IImmutableSolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Salmon"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Salmon => KnownColor.Salmon.ToBrush();
+        public static IImmutableSolidColorBrush Salmon => KnownColor.Salmon.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SandyBrown"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush();
+        public static IImmutableSolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SeaGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush();
+        public static IImmutableSolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SeaShell"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush();
+        public static IImmutableSolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Sienna"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Sienna => KnownColor.Sienna.ToBrush();
+        public static IImmutableSolidColorBrush Sienna => KnownColor.Sienna.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Silver"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Silver => KnownColor.Silver.ToBrush();
+        public static IImmutableSolidColorBrush Silver => KnownColor.Silver.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SkyBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush();
+        public static IImmutableSolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SlateBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush();
+        public static IImmutableSolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SlateGray"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush();
+        public static IImmutableSolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Snow"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Snow => KnownColor.Snow.ToBrush();
+        public static IImmutableSolidColorBrush Snow => KnownColor.Snow.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SpringGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush();
+        public static IImmutableSolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.SteelBlue"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush();
+        public static IImmutableSolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Tan"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Tan => KnownColor.Tan.ToBrush();
+        public static IImmutableSolidColorBrush Tan => KnownColor.Tan.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Teal"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Teal => KnownColor.Teal.ToBrush();
+        public static IImmutableSolidColorBrush Teal => KnownColor.Teal.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Thistle"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Thistle => KnownColor.Thistle.ToBrush();
+        public static IImmutableSolidColorBrush Thistle => KnownColor.Thistle.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Tomato"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Tomato => KnownColor.Tomato.ToBrush();
+        public static IImmutableSolidColorBrush Tomato => KnownColor.Tomato.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Transparent"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Transparent => KnownColor.Transparent.ToBrush();
+        public static IImmutableSolidColorBrush Transparent => KnownColor.Transparent.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Turquoise"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush();
+        public static IImmutableSolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Violet"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Violet => KnownColor.Violet.ToBrush();
+        public static IImmutableSolidColorBrush Violet => KnownColor.Violet.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Wheat"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Wheat => KnownColor.Wheat.ToBrush();
+        public static IImmutableSolidColorBrush Wheat => KnownColor.Wheat.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.White"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush White => KnownColor.White.ToBrush();
+        public static IImmutableSolidColorBrush White => KnownColor.White.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.WhiteSmoke"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush();
+        public static IImmutableSolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.Yellow"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush Yellow => KnownColor.Yellow.ToBrush();
+        public static IImmutableSolidColorBrush Yellow => KnownColor.Yellow.ToBrush();
 
         /// <summary>
         /// Gets an <see cref="Colors.YellowGreen"/> colored brush.
         /// </summary>
-        public static ISolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush();
+        public static IImmutableSolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush();
     }
 }

+ 1 - 1
src/Avalonia.Base/Media/ConicGradientBrush.cs

@@ -47,7 +47,7 @@ namespace Avalonia.Media
         }
 
         /// <inheritdoc/>
-        public override IBrush ToImmutable()
+        public IImmutableBrush ToImmutable()
         {
             return new ImmutableConicGradientBrush(this);
         }

+ 9 - 0
src/Avalonia.Base/Media/IImmutableBrush.cs

@@ -0,0 +1,9 @@
+namespace Avalonia.Media;
+
+/// <summary>
+/// Represents an immutable brush which can be safely used with various threading contexts
+/// </summary>
+public interface IImmutableBrush : IBrush
+{
+        
+}

+ 2 - 2
src/Avalonia.Base/Media/IMutableBrush.cs

@@ -7,12 +7,12 @@ namespace Avalonia.Media
     /// Represents a mutable brush which can return an immutable clone of itself.
     /// </summary>
     [NotClientImplementable]
-    public interface IMutableBrush : IBrush, IAffectsRender
+    internal interface IMutableBrush : IBrush, IAffectsRender
     {
         /// <summary>
         /// Creates an immutable clone of the brush.
         /// </summary>
         /// <returns>The immutable clone.</returns>
-        IBrush ToImmutable();
+        internal IImmutableBrush ToImmutable();
     }
 }

+ 9 - 0
src/Avalonia.Base/Media/ISolidColorBrush.cs

@@ -13,4 +13,13 @@ namespace Avalonia.Media
         /// </summary>
         Color Color { get; }
     }
+
+    /// <summary>
+    /// Fills an area with a solid color.
+    /// </summary>
+    [NotClientImplementable]
+    public interface IImmutableSolidColorBrush : ISolidColorBrush, IImmutableBrush
+    {
+        
+    }
 }

+ 2 - 2
src/Avalonia.Base/Media/ImageBrush.cs

@@ -6,7 +6,7 @@ namespace Avalonia.Media
     /// <summary>
     /// Paints an area with an <see cref="IBitmap"/>.
     /// </summary>
-    public class ImageBrush : TileBrush, IImageBrush
+    public class ImageBrush : TileBrush, IImageBrush, IMutableBrush
     {
         /// <summary>
         /// Defines the <see cref="Visual"/> property.
@@ -45,7 +45,7 @@ namespace Avalonia.Media
         }
 
         /// <inheritdoc/>
-        public override IBrush ToImmutable()
+        public IImmutableBrush ToImmutable()
         {
             return new ImmutableImageBrush(this);
         }

+ 373 - 0
src/Avalonia.Base/Media/ImmediateDrawingContext.cs

@@ -0,0 +1,373 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Platform;
+using Avalonia.Rendering.SceneGraph;
+using Avalonia.Threading;
+using Avalonia.Utilities;
+using Avalonia.Media.Imaging;
+using Avalonia.Media.Immutable;
+
+namespace Avalonia.Media
+{
+    public sealed class ImmediateDrawingContext : IDisposable, IOptionalFeatureProvider
+    {
+        private readonly bool _ownsImpl;
+        private int _currentLevel;
+        
+        private static ThreadSafeObjectPool<Stack<PushedState>> StateStackPool { get; } =
+            ThreadSafeObjectPool<Stack<PushedState>>.Default;
+
+        private static ThreadSafeObjectPool<Stack<TransformContainer>> TransformStackPool { get; } =
+            ThreadSafeObjectPool<Stack<TransformContainer>>.Default;
+
+        private Stack<PushedState>? _states = StateStackPool.Get();
+
+        private Stack<TransformContainer>? _transformContainers = TransformStackPool.Get();
+
+        readonly struct TransformContainer
+        {
+            public readonly Matrix LocalTransform;
+            public readonly Matrix ContainerTransform;
+
+            public TransformContainer(Matrix localTransform, Matrix containerTransform)
+            {
+                LocalTransform = localTransform;
+                ContainerTransform = containerTransform;
+            }
+        }
+
+        internal ImmediateDrawingContext(IDrawingContextImpl impl, bool ownsImpl)
+        {
+            _ownsImpl = ownsImpl;
+            PlatformImpl = impl;
+            _currentContainerTransform = impl.Transform;
+        }
+
+        public IDrawingContextImpl PlatformImpl { get; }
+
+        private Matrix _currentTransform = Matrix.Identity;
+
+        private Matrix _currentContainerTransform;
+
+        /// <summary>
+        /// Gets the current transform of the drawing context.
+        /// </summary>
+        public Matrix CurrentTransform
+        {
+            get { return _currentTransform; }
+            private set
+            {
+                _currentTransform = value;
+                var transform = _currentTransform * _currentContainerTransform;
+                PlatformImpl.Transform = transform;
+            }
+        }
+
+        /// <summary>
+        /// Draws an bitmap.
+        /// </summary>
+        /// <param name="source">The bitmap.</param>
+        /// <param name="rect">The rect in the output to draw to.</param>
+        public void DrawBitmap(IBitmap source, Rect rect)
+        {
+            _ = source ?? throw new ArgumentNullException(nameof(source));
+            DrawBitmap(source, new Rect(source.Size), rect);
+        }
+
+        /// <summary>
+        /// Draws an image.
+        /// </summary>
+        /// <param name="source">The bitmap.</param>
+        /// <param name="sourceRect">The rect in the image to draw.</param>
+        /// <param name="destRect">The rect in the output to draw to.</param>
+        /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
+        public void DrawBitmap(IBitmap source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
+        {
+            _ = source ?? throw new ArgumentNullException(nameof(source));
+            PlatformImpl.DrawBitmap(source.PlatformImpl, 1, sourceRect, destRect, bitmapInterpolationMode);
+        }
+
+        /// <summary>
+        /// Draws a line.
+        /// </summary>
+        /// <param name="pen">The stroke pen.</param>
+        /// <param name="p1">The first point of the line.</param>
+        /// <param name="p2">The second point of the line.</param>
+        public void DrawLine(ImmutablePen pen, Point p1, Point p2)
+        {
+            if (PenIsVisible(pen))
+            {
+                PlatformImpl.DrawLine(pen, p1, p2);
+            }
+        }
+
+        /// <summary>
+        /// Draws a rectangle with the specified Brush and Pen.
+        /// </summary>
+        /// <param name="brush">The brush used to fill the rectangle, or <c>null</c> for no fill.</param>
+        /// <param name="pen">The pen used to stroke the rectangle, or <c>null</c> for no stroke.</param>
+        /// <param name="rect">The rectangle bounds.</param>
+        /// <param name="radiusX">The radius in the X dimension of the rounded corners.
+        ///     This value will be clamped to the range of 0 to Width/2
+        /// </param>
+        /// <param name="radiusY">The radius in the Y dimension of the rounded corners.
+        ///     This value will be clamped to the range of 0 to Height/2
+        /// </param>
+        /// <param name="boxShadows">Box shadow effect parameters</param>
+        /// <remarks>
+        /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
+        /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
+        /// </remarks>
+        public void DrawRectangle(IImmutableBrush? brush, ImmutablePen? pen, Rect rect, double radiusX = 0, double radiusY = 0,
+            BoxShadows boxShadows = default)
+        {
+            if (brush == null && !PenIsVisible(pen))
+            {
+                return;
+            }
+
+            if (!MathUtilities.IsZero(radiusX))
+            {
+                radiusX = Math.Min(radiusX, rect.Width / 2);
+            }
+
+            if (!MathUtilities.IsZero(radiusY))
+            {
+                radiusY = Math.Min(radiusY, rect.Height / 2);
+            }
+
+            PlatformImpl.DrawRectangle(brush, pen, new RoundedRect(rect, radiusX, radiusY), boxShadows);
+        }
+
+        /// <summary>
+        /// Draws the outline of a rectangle.
+        /// </summary>
+        /// <param name="pen">The pen.</param>
+        /// <param name="rect">The rectangle bounds.</param>
+        /// <param name="cornerRadius">The corner radius.</param>
+        public void DrawRectangle(ImmutablePen pen, Rect rect, float cornerRadius = 0.0f)
+        {
+            DrawRectangle(null, pen, rect, cornerRadius, cornerRadius);
+        }
+
+        /// <summary>
+        /// Draws an ellipse with the specified Brush and Pen.
+        /// </summary>
+        /// <param name="brush">The brush used to fill the ellipse, or <c>null</c> for no fill.</param>
+        /// <param name="pen">The pen used to stroke the ellipse, or <c>null</c> for no stroke.</param>
+        /// <param name="center">The location of the center of the ellipse.</param>
+        /// <param name="radiusX">The horizontal radius of the ellipse.</param>
+        /// <param name="radiusY">The vertical radius of the ellipse.</param>
+        /// <remarks>
+        /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
+        /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
+        /// </remarks>
+        public void DrawEllipse(IImmutableBrush? brush, ImmutablePen? pen, Point center, double radiusX, double radiusY)
+        {
+            if (brush == null && !PenIsVisible(pen))
+            {
+                return;
+            }
+
+            var originX = center.X - radiusX;
+            var originY = center.Y - radiusY;
+            var width = radiusX * 2;
+            var height = radiusY * 2;
+
+            PlatformImpl.DrawEllipse(brush, pen, new Rect(originX, originY, width, height));
+        }
+
+        /// <summary>
+        /// Draws a glyph run.
+        /// </summary>
+        /// <param name="foreground">The foreground brush.</param>
+        /// <param name="glyphRun">The glyph run.</param>
+        public void DrawGlyphRun(IImmutableBrush foreground, GlyphRun glyphRun)
+        {
+            _ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun));
+
+            PlatformImpl.DrawGlyphRun(foreground, glyphRun);
+        }
+
+        /// <summary>
+        /// Draws a filled rectangle.
+        /// </summary>
+        /// <param name="brush">The brush.</param>
+        /// <param name="rect">The rectangle bounds.</param>
+        /// <param name="cornerRadius">The corner radius.</param>
+        public void FillRectangle(IImmutableBrush brush, Rect rect, float cornerRadius = 0.0f)
+        {
+            DrawRectangle(brush, null, rect, cornerRadius, cornerRadius);
+        }
+
+        public readonly record struct PushedState : IDisposable
+        {
+            private readonly int _level;
+            private readonly ImmediateDrawingContext _context;
+            private readonly Matrix _matrix;
+            private readonly PushedStateType _type;
+
+            public enum PushedStateType
+            {
+                None,
+                Matrix,
+                Opacity,
+                Clip,
+                MatrixContainer,
+                GeometryClip,
+                OpacityMask,
+            }
+
+            internal PushedState(ImmediateDrawingContext context, PushedStateType type, Matrix matrix = default(Matrix))
+            {
+                if (context._states is null)
+                    throw new ObjectDisposedException(nameof(ImmediateDrawingContext));
+
+                _context = context;
+                _type = type;
+                _matrix = matrix;
+                _level = context._currentLevel += 1;
+                context._states.Push(this);
+            }
+
+            public void Dispose()
+            {
+                if (_type == PushedStateType.None)
+                    return;
+                if (_context._states is null || _context._transformContainers is null)
+                    throw new ObjectDisposedException(nameof(DrawingContext));
+                if (_context._currentLevel != _level)
+                    throw new InvalidOperationException("Wrong Push/Pop state order");
+                _context._currentLevel--;
+                _context._states.Pop();
+                if (_type == PushedStateType.Matrix)
+                    _context.CurrentTransform = _matrix;
+                else if (_type == PushedStateType.Clip)
+                    _context.PlatformImpl.PopClip();
+                else if (_type == PushedStateType.Opacity)
+                    _context.PlatformImpl.PopOpacity();
+                else if (_type == PushedStateType.GeometryClip)
+                    _context.PlatformImpl.PopGeometryClip();
+                else if (_type == PushedStateType.OpacityMask)
+                    _context.PlatformImpl.PopOpacityMask();
+                else if (_type == PushedStateType.MatrixContainer)
+                {
+                    var cont = _context._transformContainers.Pop();
+                    _context._currentContainerTransform = cont.ContainerTransform;
+                    _context.CurrentTransform = cont.LocalTransform;
+                }
+            }
+        }
+
+
+        public PushedState PushClip(RoundedRect clip)
+        {
+            PlatformImpl.PushClip(clip);
+            return new PushedState(this, PushedState.PushedStateType.Clip);
+        }
+
+        /// <summary>
+        /// Pushes a clip rectangle.
+        /// </summary>
+        /// <param name="clip">The clip rectangle.</param>
+        /// <returns>A disposable used to undo the clip rectangle.</returns>
+        public PushedState PushClip(Rect clip)
+        {
+            PlatformImpl.PushClip(clip);
+            return new PushedState(this, PushedState.PushedStateType.Clip);
+        }
+
+        /// <summary>
+        /// Pushes an opacity value.
+        /// </summary>
+        /// <param name="opacity">The opacity.</param>
+        /// <returns>A disposable used to undo the opacity.</returns>
+        public PushedState PushOpacity(double opacity)
+        //TODO: Eliminate platform-specific push opacity call
+        {
+            PlatformImpl.PushOpacity(opacity);
+            return new PushedState(this, PushedState.PushedStateType.Opacity);
+        }
+
+        /// <summary>
+        /// Pushes an opacity mask.
+        /// </summary>
+        /// <param name="mask">The opacity mask.</param>
+        /// <param name="bounds">
+        /// The size of the brush's target area. TODO: Are we sure this is needed?
+        /// </param>
+        /// <returns>A disposable to undo the opacity mask.</returns>
+        public PushedState PushOpacityMask(IImmutableBrush mask, Rect bounds)
+        {
+            PlatformImpl.PushOpacityMask(mask, bounds);
+            return new PushedState(this, PushedState.PushedStateType.OpacityMask);
+        }
+
+        /// <summary>
+        /// Pushes a matrix post-transformation.
+        /// </summary>
+        /// <param name="matrix">The matrix</param>
+        /// <returns>A disposable used to undo the transformation.</returns>
+        public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform * matrix);
+
+        /// <summary>
+        /// Pushes a matrix pre-transformation.
+        /// </summary>
+        /// <param name="matrix">The matrix</param>
+        /// <returns>A disposable used to undo the transformation.</returns>
+        public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix * CurrentTransform);
+
+        /// <summary>
+        /// Sets the current matrix transformation.
+        /// </summary>
+        /// <param name="matrix">The matrix</param>
+        /// <returns>A disposable used to undo the transformation.</returns>
+        public PushedState PushSetTransform(Matrix matrix)
+        {
+            var oldMatrix = CurrentTransform;
+            CurrentTransform = matrix;
+
+            return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
+        }
+
+        /// <summary>
+        /// Pushes a new transform context.
+        /// </summary>
+        /// <returns>A disposable used to undo the transformation.</returns>
+        public PushedState PushTransformContainer()
+        {
+            if (_transformContainers is null)
+                throw new ObjectDisposedException(nameof(DrawingContext));
+            _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
+            _currentContainerTransform = CurrentTransform * _currentContainerTransform;
+            _currentTransform = Matrix.Identity;
+            return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
+        }
+
+        /// <summary>
+        /// Disposes of any resources held by the <see cref="DrawingContext"/>.
+        /// </summary>
+        public void Dispose()
+        {
+            if (_states is null || _transformContainers is null)
+                throw new ObjectDisposedException(nameof(DrawingContext));
+            while (_states.Count != 0)
+                _states.Peek().Dispose();
+            StateStackPool.Return(_states);
+            _states = null;
+            if (_transformContainers.Count != 0)
+                throw new InvalidOperationException("Transform container stack is non-empty");
+            TransformStackPool.Return(_transformContainers);
+            _transformContainers = null;
+            if (_ownsImpl)
+                PlatformImpl.Dispose();
+        }
+
+        private static bool PenIsVisible(IPen? pen)
+        {
+            return pen?.Brush != null && pen.Thickness > 0;
+        }
+
+        public object? TryGetFeature(Type type) => PlatformImpl.GetFeature(type);
+    }
+}

+ 1 - 1
src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs

@@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
     /// <summary>
     /// A brush that draws with a gradient.
     /// </summary>
-    public abstract class ImmutableGradientBrush : IGradientBrush
+    public abstract class ImmutableGradientBrush : IGradientBrush, IImmutableBrush
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="ImmutableGradientBrush"/> class.

+ 1 - 3
src/Avalonia.Base/Media/Immutable/ImmutablePen.cs

@@ -38,15 +38,13 @@ namespace Avalonia.Media.Immutable
         /// <param name="lineJoin">The line join.</param>
         /// <param name="miterLimit">The miter limit.</param>
         public ImmutablePen(
-            IBrush? brush,
+            IImmutableBrush? brush,
             double thickness = 1.0,
             ImmutableDashStyle? dashStyle = null,
             PenLineCap lineCap = PenLineCap.Flat,
             PenLineJoin lineJoin = PenLineJoin.Miter,
             double miterLimit = 10.0)
         {
-            Debug.Assert(!(brush is IMutableBrush));
-
             Brush = brush;
             Thickness = thickness;
             LineCap = lineCap;

+ 1 - 1
src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs

@@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
     /// <summary>
     /// Fills an area with a solid color.
     /// </summary>
-    public class ImmutableSolidColorBrush : ISolidColorBrush, IEquatable<ImmutableSolidColorBrush>
+    public class ImmutableSolidColorBrush : IImmutableSolidColorBrush, IEquatable<ImmutableSolidColorBrush>
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="ImmutableSolidColorBrush"/> class.

+ 1 - 1
src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs

@@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
     /// <summary>
     /// A brush which displays a repeating image.
     /// </summary>
-    public abstract class ImmutableTileBrush : ITileBrush
+    public abstract class ImmutableTileBrush : ITileBrush, IImmutableBrush
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="ImageBrush"/> class.

+ 3 - 3
src/Avalonia.Base/Media/KnownColors.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Media
         private static readonly IReadOnlyDictionary<string, KnownColor> _knownColorNames;
         private static readonly IReadOnlyDictionary<uint, string> _knownColors;
 #if !BUILDTASK
-        private static readonly Dictionary<KnownColor, ISolidColorBrush> _knownBrushes;
+        private static readonly Dictionary<KnownColor, IImmutableSolidColorBrush> _knownBrushes;
 #endif
 
         [GenerateEnumValueDictionary()]
@@ -39,7 +39,7 @@ namespace Avalonia.Media
             _knownColors = knownColors;
             
 #if !BUILDTASK
-            _knownBrushes = new Dictionary<KnownColor, ISolidColorBrush>();
+            _knownBrushes = new ();
 #endif
         }
 
@@ -72,7 +72,7 @@ namespace Avalonia.Media
         }
 
 #if !BUILDTASK
-        public static ISolidColorBrush ToBrush(this KnownColor color)
+        public static IImmutableSolidColorBrush ToBrush(this KnownColor color)
         {
             lock (_knownBrushes)
             {

+ 1 - 1
src/Avalonia.Base/Media/LinearGradientBrush.cs

@@ -47,7 +47,7 @@ namespace Avalonia.Media
         }
 
         /// <inheritdoc/>
-        public override IBrush ToImmutable()
+        public IImmutableBrush ToImmutable()
         {
             return new ImmutableLinearGradientBrush(this);
         }

+ 1 - 1
src/Avalonia.Base/Media/RadialGradientBrush.cs

@@ -67,7 +67,7 @@ namespace Avalonia.Media
         }
 
         /// <inheritdoc/>
-        public override IBrush ToImmutable()
+        public IImmutableBrush ToImmutable()
         {
             return new ImmutableRadialGradientBrush(this);
         }

+ 2 - 2
src/Avalonia.Base/Media/SolidColorBrush.cs

@@ -6,7 +6,7 @@ namespace Avalonia.Media
     /// <summary>
     /// Fills an area with a solid color.
     /// </summary>
-    public class SolidColorBrush : Brush, ISolidColorBrush
+    public class SolidColorBrush : Brush, ISolidColorBrush, IMutableBrush
     {
         /// <summary>
         /// Defines the <see cref="Color"/> property.
@@ -80,7 +80,7 @@ namespace Avalonia.Media
         }
 
         /// <inheritdoc/>
-        public override IBrush ToImmutable()
+        public IImmutableBrush ToImmutable()
         {
             return new ImmutableSolidColorBrush(this);
         }

+ 2 - 2
src/Avalonia.Base/Media/VisualBrush.cs

@@ -6,7 +6,7 @@ namespace Avalonia.Media
     /// <summary>
     /// Paints an area with an <see cref="Visual"/>.
     /// </summary>
-    public class VisualBrush : TileBrush, IVisualBrush
+    public class VisualBrush : TileBrush, IVisualBrush, IMutableBrush
     {
         /// <summary>
         /// Defines the <see cref="Visual"/> property.
@@ -45,7 +45,7 @@ namespace Avalonia.Media
         }
 
         /// <inheritdoc/>
-        public override IBrush ToImmutable()
+        IImmutableBrush IMutableBrush.ToImmutable()
         {
             return new ImmutableVisualBrush(this);
         }

+ 2 - 0
src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs

@@ -80,4 +80,6 @@ internal abstract class AnimationInstanceBase : IAnimationInstance
         _invalidated = true;
         TargetObject.NotifyAnimatedValueChanged(Property);
     }
+
+    public void OnTick() => Invalidate();
 }

+ 1 - 1
src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs

@@ -6,7 +6,7 @@ using Avalonia.Rendering.Composition.Server;
 
 namespace Avalonia.Rendering.Composition.Animations
 {
-    internal interface IAnimationInstance
+    internal interface IAnimationInstance : IServerClockItem
     {
         ServerObject TargetObject { get; }
         ExpressionVariant Evaluate(TimeSpan now, ExpressionVariant currentValue);

+ 37 - 0
src/Avalonia.Base/Rendering/Composition/CompositionCustomVisual.cs

@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using System.Numerics;
+using Avalonia.Rendering.Composition.Server;
+using Avalonia.Rendering.Composition.Transport;
+
+namespace Avalonia.Rendering.Composition;
+
+public class CompositionCustomVisual : CompositionContainerVisual
+{
+    private List<object>? _messages;
+
+    internal CompositionCustomVisual(Compositor compositor, CompositionCustomVisualHandler handler)
+        : base(compositor, new ServerCompositionCustomVisual(compositor.Server, handler))
+    {
+
+    }
+
+    public void SendHandlerMessage(object message)
+    {
+        (_messages ??= new()).Add(message);
+        RegisterForSerialization();
+    }
+
+    private protected override void SerializeChangesCore(BatchStreamWriter writer)
+    {
+        base.SerializeChangesCore(writer);
+        if (_messages == null || _messages.Count == 0)
+            writer.Write(0);
+        else
+        {
+            writer.Write(_messages.Count);
+            foreach (var m in _messages)
+                writer.WriteObject(m);
+            _messages.Clear();
+        }
+    }
+}

+ 36 - 0
src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Numerics;
+using Avalonia.Media;
+using Avalonia.Rendering.Composition.Server;
+
+namespace Avalonia.Rendering.Composition;
+
+public abstract class CompositionCustomVisualHandler
+{
+    private ServerCompositionCustomVisual? _host;
+
+    public virtual void OnMessage(object message)
+    {
+        
+    }
+
+    public virtual void OnAnimationFrameUpdate()
+    {
+        
+    }
+    
+    public abstract void OnRender(ImmediateDrawingContext drawingContext);
+
+    protected Vector2 EffectiveSize => _host?.Size ?? default;
+
+    protected TimeSpan CompositionNow => _host?.Compositor.ServerNow ?? default;
+    
+    public virtual Rect GetRenderBounds() =>
+        new(0, 0, EffectiveSize.X, EffectiveSize.Y);
+
+    internal void Attach(ServerCompositionCustomVisual visual) => _host = visual;
+
+    protected void Invalidate() => _host?.HandlerInvalidate();
+
+    protected void RegisterForNextAnimationFrameUpdate() => _host?.HandlerRegisterForNextAnimationFrameUpdate();
+}

+ 2 - 0
src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs

@@ -33,4 +33,6 @@ public partial class Compositor
 
     public CompositionSolidColorVisual CreateSolidColorVisual() =>
         new(this, new ServerCompositionSolidColorVisual(Server));
+
+    public CompositionCustomVisual CreateCustomVisual(CompositionCustomVisualHandler handler) => new(this, handler);
 }

+ 6 - 0
src/Avalonia.Base/Rendering/Composition/Server/IServerClockItem.cs

@@ -0,0 +1,6 @@
+namespace Avalonia.Rendering.Composition.Server;
+
+internal interface IServerClockItem
+{
+    void OnTick();
+}

+ 0 - 2
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@@ -87,8 +87,6 @@ namespace Avalonia.Rendering.Composition.Server
             }
 
             _renderTarget ??= _compositor.CreateRenderTarget(_surfaces());
-
-            Compositor.UpdateServerTime();
             
             if(_dirtyRect.IsEmpty && !_redrawRequested)
                 return;

+ 16 - 0
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@@ -216,13 +216,29 @@ namespace Avalonia.Rendering.Composition.Server
         partial void OnRootChanging()
         {
             if (Root != null)
+            {
                 Root.RemoveVisual(this);
+                OnDetachedFromRoot(Root);
+            }
+        }
+
+        protected virtual void OnDetachedFromRoot(ServerCompositionTarget target)
+        {
+            
         }
         
         partial void OnRootChanged()
         {
             if (Root != null)
+            {
                 Root.AddVisual(this);
+                OnAttachedToRoot(Root);
+            }
+        }
+        
+        protected virtual void OnAttachedToRoot(ServerCompositionTarget target)
+        {
+            
         }
         
         protected override void ValuesInvalidated()

+ 13 - 12
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs

@@ -26,8 +26,8 @@ namespace Avalonia.Rendering.Composition.Server
         public Stopwatch Clock { get; } = Stopwatch.StartNew();
         public TimeSpan ServerNow { get; private set; }
         private List<ServerCompositionTarget> _activeTargets = new();
-        private HashSet<IAnimationInstance> _activeAnimations = new();
-        private List<IAnimationInstance> _animationsToUpdate = new();
+        private HashSet<IServerClockItem> _clockItems = new();
+        private List<IServerClockItem> _clockItemsToUpdate = new();
         internal BatchStreamObjectPool<object?> BatchObjectPool;
         internal BatchStreamMemoryPool BatchMemoryPool;
         private object _lock = new object();
@@ -135,16 +135,17 @@ namespace Avalonia.Rendering.Composition.Server
         
         private void RenderCore()
         {
+            UpdateServerTime();
             ApplyPendingBatches();
             CompletePendingBatches();
             
-            foreach(var animation in _activeAnimations)
-                _animationsToUpdate.Add(animation);
-            
-            foreach(var animation in _animationsToUpdate)
-                animation.Invalidate();
+            foreach(var animation in _clockItems)
+                _clockItemsToUpdate.Add(animation);
+
+            foreach (var animation in _clockItemsToUpdate)
+                animation.OnTick();
             
-            _animationsToUpdate.Clear();
+            _clockItemsToUpdate.Clear();
             
             try
             {
@@ -168,11 +169,11 @@ namespace Avalonia.Rendering.Composition.Server
             _activeTargets.Remove(target);
         }
         
-        public void AddToClock(IAnimationInstance animationInstance) =>
-            _activeAnimations.Add(animationInstance);
+        public void AddToClock(IServerClockItem item) =>
+            _clockItems.Add(item);
 
-        public void RemoveFromClock(IAnimationInstance animationInstance) =>
-            _activeAnimations.Remove(animationInstance);
+        public void RemoveFromClock(IServerClockItem item) =>
+            _clockItems.Remove(item);
 
         public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
         {

+ 74 - 0
src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Numerics;
+using Avalonia.Logging;
+using Avalonia.Media;
+using Avalonia.Rendering.Composition.Transport;
+
+namespace Avalonia.Rendering.Composition.Server;
+
+internal class ServerCompositionCustomVisual : ServerCompositionContainerVisual, IServerClockItem
+{
+    private readonly CompositionCustomVisualHandler _handler;
+    private bool _wantsNextAnimationFrameAfterTick;
+    internal ServerCompositionCustomVisual(ServerCompositor compositor, CompositionCustomVisualHandler handler) : base(compositor)
+    {
+        _handler = handler;
+        _handler.Attach(this);
+    }
+
+    protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt)
+    {
+        base.DeserializeChangesCore(reader, commitedAt);
+        var count = reader.Read<int>();
+        for (var c = 0; c < count; c++)
+        {
+            try
+            {
+                _handler.OnMessage(reader.ReadObject()!);
+            }
+            catch (Exception e)
+            {
+                Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
+                    ?.Log(_handler, $"Exception in {nameof(CompositionCustomVisualHandler)} {{0}}", e);
+            }
+        }
+    }
+
+    public void OnTick()
+    {
+        _wantsNextAnimationFrameAfterTick = false;
+        _handler.OnAnimationFrameUpdate();
+        if (!_wantsNextAnimationFrameAfterTick)
+            Compositor.RemoveFromClock(this);
+    }
+
+    public override Rect OwnContentBounds => _handler.GetRenderBounds();
+
+    protected override void OnAttachedToRoot(ServerCompositionTarget target)
+    {
+        if (_wantsNextAnimationFrameAfterTick)
+            Compositor.AddToClock(this);
+        base.OnAttachedToRoot(target);
+    }
+
+    protected override void OnDetachedFromRoot(ServerCompositionTarget target)
+    {
+        Compositor.RemoveFromClock(this);
+        base.OnDetachedFromRoot(target);
+    }
+
+    internal void HandlerInvalidate() => ValuesInvalidated();
+    
+    internal void HandlerRegisterForNextAnimationFrameUpdate()
+    {
+        _wantsNextAnimationFrameAfterTick = true;
+        if (Root != null)
+            Compositor.AddToClock(this);
+    }
+
+    protected override void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip)
+    {
+        var context = new ImmediateDrawingContext(canvas, false);
+        _handler.OnRender(context);
+    }
+}

+ 1 - 1
src/Avalonia.Base/Rendering/Composition/VisualCollection.cs

@@ -69,7 +69,7 @@ namespace Avalonia.Rendering.Composition
         {
             if (item.Parent != null)
                 throw new InvalidOperationException("Visual already has a parent");
-            item.Parent = item;
+            item.Parent = _owner;
         }
     }
 }

+ 3 - 0
src/Avalonia.Base/Visual.cs

@@ -540,6 +540,9 @@ namespace Avalonia
             OnDetachedFromVisualTree(e);
             if (CompositionVisual != null)
             {
+                if (ChildCompositionVisual != null)
+                    CompositionVisual.Children.Remove(ChildCompositionVisual);
+                
                 CompositionVisual.DrawList = null;
                 CompositionVisual = null;
             }