diff --git a/samples/ControlCatalog/Pages/ImagePage.xaml b/samples/ControlCatalog/Pages/ImagePage.xaml index c20f76cedd..f61931ed72 100644 --- a/samples/ControlCatalog/Pages/ImagePage.xaml +++ b/samples/ControlCatalog/Pages/ImagePage.xaml @@ -23,7 +23,7 @@ Crop - + None Center TopLeft @@ -31,7 +31,11 @@ BottomLeft BottomRight - + + + + + diff --git a/samples/ControlCatalog/Pages/ImagePage.xaml.cs b/samples/ControlCatalog/Pages/ImagePage.xaml.cs index d637c88102..d8f4d6d5a2 100644 --- a/samples/ControlCatalog/Pages/ImagePage.xaml.cs +++ b/samples/ControlCatalog/Pages/ImagePage.xaml.cs @@ -13,7 +13,6 @@ namespace ControlCatalog.Pages private readonly Image _bitmapImage; private readonly Image _drawingImage; private readonly Image _croppedImage; - private readonly IBitmap _croppedBitmapSource; public ImagePage() { @@ -21,8 +20,6 @@ namespace ControlCatalog.Pages _bitmapImage = this.FindControl("bitmapImage"); _drawingImage = this.FindControl("drawingImage"); _croppedImage = this.FindControl("croppedImage"); - _croppedBitmapSource = LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"); - _croppedImage.Source = new CroppedBitmap(_croppedBitmapSource, default); } private void InitializeComponent() @@ -53,15 +50,16 @@ namespace ControlCatalog.Pages if (_croppedImage != null) { var comboxBox = (ComboBox)sender; - _croppedImage.Source = new CroppedBitmap( _croppedBitmapSource, GetCropRect(comboxBox.SelectedIndex)); + var croppedBitmap = _croppedImage.Source as CroppedBitmap; + croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex); } } private PixelRect GetCropRect(int index) { - var bitmapWidth = _croppedBitmapSource.PixelSize.Width; - var bitmapHeight = _croppedBitmapSource.PixelSize.Height; - var cropSize = new PixelSize(bitmapWidth / 2, bitmapHeight / 2); + var bitmapWidth = 640; + var bitmapHeight = 426; + var cropSize = new PixelSize(320, 240); return index switch { 1 => new PixelRect(new PixelPoint((bitmapWidth - cropSize.Width) / 2, (bitmapHeight - cropSize.Width) / 2), cropSize), @@ -73,11 +71,5 @@ namespace ControlCatalog.Pages }; } - - private IBitmap LoadBitmap(string uri) - { - var assets = AvaloniaLocator.Current.GetService(); - return new Bitmap(assets.Open(new Uri(uri))); - } } } diff --git a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs index 6bdee24c03..70823e114c 100644 --- a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs @@ -1,25 +1,77 @@ using System; -using System.Collections.Generic; -using System.Text; using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media.Imaging { - public class CroppedBitmap : IImage, IDisposable + /// + /// Crops a Bitmap. + /// + public class CroppedBitmap : AvaloniaObject, IImage, IAffectsRender, IDisposable { + /// + /// Defines the property. + /// + public static readonly StyledProperty SourceProperty = + AvaloniaProperty.Register(nameof(Source)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty SourceRectProperty = + AvaloniaProperty.Register(nameof(SourceRect)); + + public event EventHandler Invalidated; + + static CroppedBitmap() + { + SourceRectProperty.Changed.AddClassHandler((x, e) => x.SourceRectChanged(e)); + SourceProperty.Changed.AddClassHandler((x, e) => x.SourceChanged(e)); + } + + /// + /// Gets or sets the source for the bitmap. + /// + public IImage Source + { + get => GetValue(SourceProperty); + set => SetValue(SourceProperty, value); + } + + /// + /// Gets or sets the rectangular area that the bitmap is cropped to. + /// + public PixelRect SourceRect + { + get => GetValue(SourceRectProperty); + set => SetValue(SourceRectProperty, value); + } + public CroppedBitmap() { Source = null; SourceRect = default; } - public CroppedBitmap(IBitmap source, PixelRect sourceRect) + + public CroppedBitmap(IImage source, PixelRect sourceRect) { Source = source; SourceRect = sourceRect; } + + private void SourceChanged(AvaloniaPropertyChangedEventArgs e) + { + if (e.NewValue == null) + return; + if (!(e.NewValue is IBitmap)) + throw new ArgumentException("Only IBitmap supported as source"); + Invalidated?.Invoke(this, e); + } + + private void SourceRectChanged(AvaloniaPropertyChangedEventArgs e) => Invalidated?.Invoke(this, e); + public virtual void Dispose() { - Source?.Dispose(); + (Source as IBitmap)?.Dispose(); } public Size Size { @@ -29,7 +81,7 @@ namespace Avalonia.Media.Imaging return Size.Empty; if (SourceRect.IsEmpty) return Source.Size; - return SourceRect.Size.ToSizeWithDpi(Source.Dpi); + return SourceRect.Size.ToSizeWithDpi((Source as IBitmap).Dpi); } } @@ -37,11 +89,8 @@ namespace Avalonia.Media.Imaging { if (Source == null) return; - var topLeft = SourceRect.TopLeft.ToPointWithDpi(Source.Dpi); + var topLeft = SourceRect.TopLeft.ToPointWithDpi((Source as IBitmap).Dpi); Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect, bitmapInterpolationMode); } - - public IBitmap Source { get; } - public PixelRect SourceRect { get; } } } diff --git a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs index 05c3d7e62a..8f08bc5f44 100644 --- a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs @@ -8,7 +8,8 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Visuals.UnitTests")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Imaging")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] [assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests")] -[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests")] \ No newline at end of file +[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests")]