using System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using AsyncImageLoader; using Avalonia; using Avalonia.Layout; using Avalonia.Media; namespace StabilityMatrix.Avalonia.Controls; [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] [SuppressMessage("ReSharper", "UnusedMember.Local")] public class BetterAdvancedImage : AdvancedImage { #region Reflection Shenanigans to access private parent fields [NotNull] private static readonly FieldInfo? IsCornerRadiusUsedField = typeof(AdvancedImage).GetField( "_isCornerRadiusUsed", BindingFlags.Instance | BindingFlags.NonPublic ); [NotNull] private static readonly FieldInfo? CornerRadiusClipField = typeof(AdvancedImage).GetField( "_cornerRadiusClip", BindingFlags.Instance | BindingFlags.NonPublic ); private bool IsCornerRadiusUsed { get => IsCornerRadiusUsedField.GetValue(this) as bool? ?? false; set => IsCornerRadiusUsedField.SetValue(this, value); } private RoundedRect CornerRadiusClip { get => (RoundedRect)CornerRadiusClipField.GetValue(this)!; set => CornerRadiusClipField.SetValue(this, value); } static BetterAdvancedImage() { if (IsCornerRadiusUsedField is null) { throw new NullReferenceException("IsCornerRadiusUsedField was not resolved"); } if (CornerRadiusClipField is null) { throw new NullReferenceException("CornerRadiusClipField was not resolved"); } } #endregion protected override Type StyleKeyOverride { get; } = typeof(AdvancedImage); public BetterAdvancedImage(Uri? baseUri) : base(baseUri) { } public BetterAdvancedImage(IServiceProvider serviceProvider) : base(serviceProvider) { } /// /// public override void Render(DrawingContext context) { var source = CurrentImage; if (source != null && Bounds is { Width: > 0, Height: > 0 }) { var viewPort = new Rect(Bounds.Size); var sourceSize = source.Size; var scale = Stretch.CalculateScaling(Bounds.Size, sourceSize, StretchDirection); var scaledSize = sourceSize * scale; // Calculate starting points for dest var destX = HorizontalContentAlignment switch { HorizontalAlignment.Left => 0, HorizontalAlignment.Center => (int)(viewPort.Width - scaledSize.Width) / 2, HorizontalAlignment.Right => (int)(viewPort.Width - scaledSize.Width), // Stretch is default, use center HorizontalAlignment.Stretch => (int)(viewPort.Width - scaledSize.Width) / 2, _ => throw new ArgumentException(nameof(HorizontalContentAlignment)) }; var destY = VerticalContentAlignment switch { VerticalAlignment.Top => 0, VerticalAlignment.Center => (int)(viewPort.Height - scaledSize.Height) / 2, VerticalAlignment.Bottom => (int)(viewPort.Height - scaledSize.Height), VerticalAlignment.Stretch => 0, // Stretch is default, use top _ => throw new ArgumentException(nameof(VerticalContentAlignment)) }; var destRect = viewPort .CenterRect(new Rect(scaledSize)) .WithX(destX) .WithY(destY) .Intersect(viewPort); var destRectUnscaledSize = destRect.Size / scale; // Calculate starting points for source var sourceX = HorizontalContentAlignment switch { HorizontalAlignment.Left => 0, HorizontalAlignment.Center => (int)(sourceSize - destRectUnscaledSize).Width / 2, HorizontalAlignment.Right => (int)(sourceSize - destRectUnscaledSize).Width, // Stretch is default, use center HorizontalAlignment.Stretch => (int)(sourceSize - destRectUnscaledSize).Width / 2, _ => throw new ArgumentException(nameof(HorizontalContentAlignment)) }; var sourceY = VerticalContentAlignment switch { VerticalAlignment.Top => 0, VerticalAlignment.Center => (int)(sourceSize - destRectUnscaledSize).Height / 2, VerticalAlignment.Bottom => (int)(sourceSize - destRectUnscaledSize).Height, VerticalAlignment.Stretch => 0, // Stretch is default, use top _ => throw new ArgumentException(nameof(VerticalContentAlignment)) }; var sourceRect = new Rect(sourceSize) .CenterRect(new Rect(destRect.Size / scale)) .WithX(sourceX) .WithY(sourceY); if (IsCornerRadiusUsed) { using (context.PushClip(CornerRadiusClip)) { context.DrawImage(source, sourceRect, destRect); } } else { context.DrawImage(source, sourceRect, destRect); } } else { base.Render(context); } } }