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);
}
}
}