* Fixed relative mapping mode for brushes (skia only, pending radial) * Implemented separate RadiusX/RadiusY with proper mapping modes for RadialGradientBrush * tests for conic brush * Added tests for geometry drawing * Updated DrawingBrush test image since it now matches WPF * Update obsolete property usage * Fixed D2D, updated radial test with D2D results that actually match WPF ones * Fixed RadiusX/Y for radial gradients with non-centered origin * Updated obsolete property usage * Code cleanup * Typo * ApiDiff suppression * Removed files for skipped test * More info in obsoletion warning * clarify --------- Co-authored-by: Jumar Macato <16554748+jmacato@users.noreply.github.com>pull/14564/head
@ -0,0 +1,20 @@ |
|||
namespace Avalonia.Animation.Animators |
|||
{ |
|||
/// <summary>
|
|||
/// Animator that handles <see cref="RelativeScalar"/> properties.
|
|||
/// </summary>
|
|||
internal class RelativeScalarAnimator : Animator<RelativeScalar> |
|||
{ |
|||
private static readonly DoubleAnimator s_scalarAnimator = new DoubleAnimator(); |
|||
|
|||
public override RelativeScalar Interpolate(double progress, RelativeScalar oldValue, RelativeScalar newValue) |
|||
{ |
|||
if (oldValue.Unit != newValue.Unit) |
|||
{ |
|||
return progress >= 0.5 ? newValue : oldValue; |
|||
} |
|||
|
|||
return new RelativeScalar(s_scalarAnimator.Interpolate(progress, oldValue.Scalar, newValue.Scalar), oldValue.Unit); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia; |
|||
|
|||
/// <summary>
|
|||
/// Defines a scalar value that may be defined relative to a containing element.
|
|||
/// </summary>
|
|||
public struct RelativeScalar : IEquatable<RelativeScalar> |
|||
{ |
|||
private readonly double _scalar; |
|||
|
|||
private readonly RelativeUnit _unit; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RelativeScalar"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="scalar">The scalar value.</param>
|
|||
/// <param name="unit">The unit.</param>
|
|||
public RelativeScalar(double scalar, RelativeUnit unit) |
|||
{ |
|||
_scalar = scalar; |
|||
_unit = unit; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the scalar.
|
|||
/// </summary>
|
|||
public double Scalar => _scalar; |
|||
|
|||
/// <summary>
|
|||
/// Gets the unit.
|
|||
/// </summary>
|
|||
public RelativeUnit Unit => _unit; |
|||
|
|||
/// <summary>
|
|||
/// The value at the beginning of the range
|
|||
/// </summary>
|
|||
public static RelativeScalar Beginning { get; } = new RelativeScalar(0, RelativeUnit.Relative); |
|||
|
|||
/// <summary>
|
|||
/// The value at the middle of the range
|
|||
/// </summary>
|
|||
public static RelativeScalar Middle { get; } = new RelativeScalar(0.5, RelativeUnit.Relative); |
|||
|
|||
/// <summary>
|
|||
/// The value at the end of the range
|
|||
/// </summary>
|
|||
public static RelativeScalar End { get; } = new RelativeScalar(1, RelativeUnit.Relative); |
|||
|
|||
public bool Equals(RelativeScalar other) |
|||
{ |
|||
return _scalar.Equals(other._scalar) && _unit == other._unit; |
|||
} |
|||
|
|||
public override bool Equals(object? obj) |
|||
{ |
|||
return obj is RelativeScalar other && Equals(other); |
|||
} |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
return _scalar.GetHashCode() ^ (int)_unit; |
|||
} |
|||
|
|||
public static bool operator ==(RelativeScalar left, RelativeScalar right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
public static bool operator !=(RelativeScalar left, RelativeScalar right) |
|||
{ |
|||
return !left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts a <see cref="RelativeScalar"/> into a final value.
|
|||
/// </summary>
|
|||
/// <returns>The origin point in pixels.</returns>
|
|||
public double ToValue(double size) |
|||
{ |
|||
return _unit == RelativeUnit.Absolute |
|||
? _scalar |
|||
: size * _scalar; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parses a <see cref="RelativeScalar"/> string.
|
|||
/// </summary>
|
|||
/// <param name="s">The string.</param>
|
|||
/// <returns>The parsed <see cref="RelativeScalar"/>.</returns>
|
|||
public static RelativeScalar Parse(string s) |
|||
{ |
|||
var trimmed = s.Trim(); |
|||
if (trimmed.EndsWith("%")) |
|||
return new RelativeScalar(double.Parse(trimmed.TrimEnd('%'), CultureInfo.InvariantCulture) * 0.01, |
|||
RelativeUnit.Relative); |
|||
|
|||
return new RelativeScalar(double.Parse(trimmed, CultureInfo.InvariantCulture), RelativeUnit.Absolute); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a String representing this RelativeScalar instance.
|
|||
/// </summary>
|
|||
/// <returns>The string representation.</returns>
|
|||
public override string ToString() |
|||
{ |
|||
return _unit == RelativeUnit.Absolute |
|||
? _scalar.ToString(CultureInfo.InvariantCulture) |
|||
: string.Format(CultureInfo.InvariantCulture, "{0}%", _scalar * 100); |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
#nullable enable |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Shapes; |
|||
using Avalonia.Layout; |
|||
using Avalonia.Media; |
|||
using Avalonia.Media.Imaging; |
|||
using Xunit; |
|||
|
|||
#if AVALONIA_SKIA
|
|||
namespace Avalonia.Skia.RenderTests |
|||
#else
|
|||
namespace Avalonia.Direct2D1.RenderTests.Media |
|||
#endif
|
|||
{ |
|||
public class RelativePointTestPrimitivesHelper : Control |
|||
{ |
|||
private readonly IBrush? _brush; |
|||
private readonly bool _shadow; |
|||
private readonly IPen _line; |
|||
private static readonly Geometry s_Geometry = Geometry.Parse("m 80 200 c 40 20 150 -40 160 0 l 0 30 c -40 -30 -160 10 -160 -30 z"); |
|||
|
|||
public RelativePointTestPrimitivesHelper(IBrush? brush, bool shadow = false) |
|||
{ |
|||
_brush = brush; |
|||
_shadow = shadow; |
|||
if (brush != null) |
|||
_line = new Pen(brush, 10); |
|||
|
|||
MinHeight = MaxHeight = Height = MinWidth = MaxWidth = Width = 256; |
|||
} |
|||
|
|||
public override void Render(DrawingContext context) |
|||
{ |
|||
if (_shadow) |
|||
{ |
|||
var full = new Rect(default, Bounds.Size); |
|||
context.DrawRectangle(Brushes.White, null, full); |
|||
using (context.PushOpacity(0.3)) |
|||
context.DrawRectangle(_brush, null, full); |
|||
} |
|||
|
|||
context.DrawRectangle(_brush, null, new Rect(20, 20, 200, 60)); |
|||
context.DrawEllipse(_brush, null, new Rect(40, 100, 200, 20)); |
|||
context.DrawLine(_line, new Point(60, 140), new Point(240, 160)); |
|||
context.DrawGeometry(_brush, null, s_Geometry); |
|||
|
|||
base.Render(context); |
|||
} |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 8.9 KiB |