From c34bfc56f8d3baa8b7f3e6d116e27edaa9a46a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D0=B4=D0=B8=D0=BC=20=D0=9C=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= Date: Tue, 18 Feb 2020 16:25:28 +0300 Subject: [PATCH 1/2] Add class CroppedBitmap --- samples/ControlCatalog/Pages/ImagePage.xaml | 15 +++++- .../ControlCatalog/Pages/ImagePage.xaml.cs | 41 ++++++++++++++++ .../Media/Imaging/CroppedBitmap.cs | 47 +++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs diff --git a/samples/ControlCatalog/Pages/ImagePage.xaml b/samples/ControlCatalog/Pages/ImagePage.xaml index 9b8f8af765..c20f76cedd 100644 --- a/samples/ControlCatalog/Pages/ImagePage.xaml +++ b/samples/ControlCatalog/Pages/ImagePage.xaml @@ -7,7 +7,7 @@ Displays an image - + Bitmap @@ -22,6 +22,19 @@ + Crop + + None + Center + TopLeft + TopRight + BottomLeft + BottomRight + + + + + Drawing None diff --git a/samples/ControlCatalog/Pages/ImagePage.xaml.cs b/samples/ControlCatalog/Pages/ImagePage.xaml.cs index bbe89d1dfd..d637c88102 100644 --- a/samples/ControlCatalog/Pages/ImagePage.xaml.cs +++ b/samples/ControlCatalog/Pages/ImagePage.xaml.cs @@ -1,6 +1,10 @@ +using System; +using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; namespace ControlCatalog.Pages { @@ -8,12 +12,17 @@ namespace ControlCatalog.Pages { private readonly Image _bitmapImage; private readonly Image _drawingImage; + private readonly Image _croppedImage; + private readonly IBitmap _croppedBitmapSource; public ImagePage() { InitializeComponent(); _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() @@ -38,5 +47,37 @@ namespace ControlCatalog.Pages _drawingImage.Stretch = (Stretch)comboxBox.SelectedIndex; } } + + public void BitmapCropChanged(object sender, SelectionChangedEventArgs e) + { + if (_croppedImage != null) + { + var comboxBox = (ComboBox)sender; + _croppedImage.Source = new CroppedBitmap( _croppedBitmapSource, 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); + return index switch + { + 1 => new PixelRect(new PixelPoint((bitmapWidth - cropSize.Width) / 2, (bitmapHeight - cropSize.Width) / 2), cropSize), + 2 => new PixelRect(new PixelPoint(0, 0), cropSize), + 3 => new PixelRect(new PixelPoint(bitmapWidth - cropSize.Width, 0), cropSize), + 4 => new PixelRect(new PixelPoint(0, bitmapHeight - cropSize.Height), cropSize), + 5 => new PixelRect(new PixelPoint(bitmapWidth - cropSize.Width, bitmapHeight - cropSize.Height), cropSize), + _ => PixelRect.Empty + }; + + } + + 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 new file mode 100644 index 0000000000..6bdee24c03 --- /dev/null +++ b/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Visuals.Media.Imaging; + +namespace Avalonia.Media.Imaging +{ + public class CroppedBitmap : IImage, IDisposable + { + public CroppedBitmap() + { + Source = null; + SourceRect = default; + } + public CroppedBitmap(IBitmap source, PixelRect sourceRect) + { + Source = source; + SourceRect = sourceRect; + } + public virtual void Dispose() + { + Source?.Dispose(); + } + + public Size Size { + get + { + if (Source == null) + return Size.Empty; + if (SourceRect.IsEmpty) + return Source.Size; + return SourceRect.Size.ToSizeWithDpi(Source.Dpi); + } + } + + public void Draw(DrawingContext context, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode) + { + if (Source == null) + return; + var topLeft = SourceRect.TopLeft.ToPointWithDpi(Source.Dpi); + Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect, bitmapInterpolationMode); + } + + public IBitmap Source { get; } + public PixelRect SourceRect { get; } + } +} From 3aab2d5619a09835e48d3abe8106f8f543743cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D0=B4=D0=B8=D0=BC=20=D0=9C=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= Date: Mon, 2 Mar 2020 10:40:41 +0300 Subject: [PATCH 2/2] Added functionality to use CroppedBitmap directly in XAML --- samples/ControlCatalog/Pages/ImagePage.xaml | 8 ++- .../ControlCatalog/Pages/ImagePage.xaml.cs | 18 ++--- .../Media/Imaging/CroppedBitmap.cs | 69 ++++++++++++++++--- .../Properties/AssemblyInfo.cs | 3 +- 4 files changed, 72 insertions(+), 26 deletions(-) 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")]