diff --git a/samples/ControlCatalog/Pages/BorderPage.xaml b/samples/ControlCatalog/Pages/BorderPage.xaml
index 4c2384d686..c811ddaa22 100644
--- a/samples/ControlCatalog/Pages/BorderPage.xaml
+++ b/samples/ControlCatalog/Pages/BorderPage.xaml
@@ -20,26 +20,26 @@
Background And CenterBorder
- Background And InnerBorder
+ Background And InnerBorderEdge
Background And OuterBorderEdge
Rounded Corners
@@ -56,6 +56,6 @@
-
+
diff --git a/src/Avalonia.Base/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs
index 8c3491ed33..fb9dca26c9 100644
--- a/src/Avalonia.Base/Media/Brush.cs
+++ b/src/Avalonia.Base/Media/Brush.cs
@@ -1,9 +1,7 @@
using System;
using System.ComponentModel;
using Avalonia.Animation;
-using Avalonia.Animation.Animators;
using Avalonia.Media.Immutable;
-using Avalonia.Reactive;
using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Drawing;
using Avalonia.Rendering.Composition.Server;
@@ -40,8 +38,8 @@ namespace Avalonia.Media
///
public double Opacity
{
- get { return GetValue(OpacityProperty); }
- set { SetValue(OpacityProperty, value); }
+ get => GetValue(OpacityProperty);
+ set => SetValue(OpacityProperty, value);
}
///
@@ -49,8 +47,8 @@ namespace Avalonia.Media
///
public ITransform? Transform
{
- get { return GetValue(TransformProperty); }
- set { SetValue(TransformProperty, value); }
+ get => GetValue(TransformProperty);
+ set => SetValue(TransformProperty, value);
}
///
@@ -73,28 +71,30 @@ namespace Avalonia.Media
if (s.Length > 0)
{
- if (s[0] == '#')
- {
- return new ImmutableSolidColorBrush(Color.Parse(s));
- }
-
+ // Attempt to get a cached known brush first
+ // This is a performance optimization for known colors
var brush = KnownColors.GetKnownBrush(s);
if (brush != null)
{
return brush;
}
+
+ if (Color.TryParse(s, out Color color))
+ {
+ return new ImmutableSolidColorBrush(color);
+ }
}
throw new FormatException($"Invalid brush string: '{s}'.");
}
-
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Property == TransformProperty)
_resource.ProcessPropertyChangeNotification(change);
RegisterForSerialization();
-
+
base.OnPropertyChanged(change);
}
@@ -126,7 +126,7 @@ namespace Avalonia.Media
if(_resource.Release(c))
OnUnreferencedFromCompositor(c);
}
-
+
protected virtual void OnUnreferencedFromCompositor(Compositor c)
{
if (Transform is ICompositionRenderResource resource)
@@ -139,7 +139,7 @@ namespace Avalonia.Media
{
ServerCompositionSimpleBrush.SerializeAllChanges(writer, Opacity, TransformOrigin, Transform.GetServer(c));
}
-
+
void ICompositorSerializable.SerializeChanges(Compositor c, BatchStreamWriter writer) => SerializeChanges(c, writer);
}
}
diff --git a/src/Avalonia.Base/Media/KnownColors.cs b/src/Avalonia.Base/Media/KnownColors.cs
index a0dea275bd..192eda9bc4 100644
--- a/src/Avalonia.Base/Media/KnownColors.cs
+++ b/src/Avalonia.Base/Media/KnownColors.cs
@@ -1,8 +1,7 @@
using System;
-using System.Reflection;
using System.Collections.Generic;
-using Avalonia.SourceGenerator;
using System.Diagnostics.CodeAnalysis;
+using Avalonia.SourceGenerator;
namespace Avalonia.Media
{
@@ -45,13 +44,47 @@ namespace Avalonia.Media
}
#if !BUILDTASK
+
+ ///
+ /// Attempts to resolve a color name string to a solid color brush.
+ ///
+ ///
+ ///
+ /// Returns a cached immutable brush if the name matches one of the predefined
+ /// values. Repeated calls with the same color name will
+ /// return the same brush instance.
+ ///
+ ///
+ /// The lookup is case-sensitive and depends on the set of predefined known colors.
+ ///
+ ///
+ ///
+ /// The color name to look up (for example, "Red" or "CornflowerBlue").
+ ///
+ ///
+ /// An corresponding to the specified name,
+ /// or null if the color name is not recognized.
+ ///
public static ISolidColorBrush? GetKnownBrush(string s)
{
var color = GetKnownColor(s);
return color != KnownColor.None ? color.ToBrush() : null;
}
+
#endif
+ ///
+ /// Attempts to resolve a color name string to a value.
+ ///
+ ///
+ /// The lookup is case-sensitive and depends on the set of predefined known colors.
+ ///
+ ///
+ /// The color name to look up (for example, Red or CornflowerBlue).
+ ///
+ ///
+ /// A value if the name is recognized; otherwise .
+ ///
public static KnownColor GetKnownColor(string s)
{
if (_knownColorNames.TryGetValue(s, out var color))
@@ -76,6 +109,20 @@ namespace Avalonia.Media
}
#if !BUILDTASK
+
+ ///
+ /// Converts a value to an immutable solid color brush.
+ ///
+ ///
+ /// This method maintains an internal cache of brushes to avoid unnecessary allocations.
+ /// If the same is requested multiple times, the same immutable brush
+ /// instance is returned.
+ ///
+ /// The to convert.
+ ///
+ /// An instance representing the specified color.
+ /// Brushes created from known colors are cached and reused for efficiency.
+ ///
public static IImmutableSolidColorBrush ToBrush(this KnownColor color)
{
lock (_knownBrushes)
@@ -89,9 +136,14 @@ namespace Avalonia.Media
return brush;
}
}
+
#endif
+
}
+ ///
+ /// Defines all known colors by name along with their 32-bit ARGB value.
+ ///
internal enum KnownColor : uint
{
None,
diff --git a/tests/Avalonia.Base.UnitTests/Media/BrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/BrushTests.cs
index 6b103ca684..c9c5b76b27 100644
--- a/tests/Avalonia.Base.UnitTests/Media/BrushTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Media/BrushTests.cs
@@ -1,6 +1,5 @@
using System;
using Avalonia.Media;
-using Avalonia.Rendering.Composition.Drawing;
using Xunit;
namespace Avalonia.Base.UnitTests.Media
@@ -79,6 +78,27 @@ namespace Avalonia.Base.UnitTests.Media
Assert.Throws(() => Brush.Parse("#ff808g80"));
}
+ [Theory]
+ [InlineData("rgb(255, 128, 64)")]
+ [InlineData("rgba(255, 128, 64, 0.5)")]
+ [InlineData("hsl(120, 100%, 50%)")]
+ [InlineData("hsla(120, 100%, 50%, 0.5)")]
+ [InlineData("hsv(300, 100%, 25%)")]
+ [InlineData("hsva(300, 100%, 25%, 0.75)")]
+ [InlineData("#40ff8844")]
+ [InlineData("Green")]
+ public void Parse_Parses_All_Color_Format_Brushes(string input)
+ {
+ var brush = Brush.Parse(input);
+ Assert.IsAssignableFrom(brush);
+
+ // The ColorTests already validate all color formats are parsed properly
+ // Since Brush.Parse() forwards to Color.Parse() we don't need to repeat this
+ // We can simply check if the parsed Brush's color matches what Color.Parse provides
+ var expected = Color.Parse(input);
+ Assert.Equal(expected, (brush as ISolidColorBrush)?.Color);
+ }
+
[Fact]
public void Changing_Opacity_Raises_Invalidated()
{