From a0e8dca6e97bc55f8b68df2eef020457849cf939 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 11 Sep 2018 22:47:03 +0200 Subject: [PATCH 1/4] Use PixelSize for device-dependent sizes. --- src/Avalonia.Controls/Image.cs | 5 +- src/Avalonia.Controls/Remote/RemoteWidget.cs | 8 +- .../Remote/Server/RemoteServerTopLevelImpl.cs | 2 +- src/Avalonia.Visuals/Avalonia.Visuals.csproj | 1 + src/Avalonia.Visuals/Media/Imaging/Bitmap.cs | 31 ++-- src/Avalonia.Visuals/Media/Imaging/IBitmap.cs | 21 ++- .../Media/Imaging/RenderTargetBitmap.cs | 29 +-- .../Media/Imaging/WriteableBitmap.cs | 11 +- src/Avalonia.Visuals/Media/PixelSize.cs | 174 ++++++++++++++++++ src/Avalonia.Visuals/Platform/IBitmapImpl.cs | 8 +- .../Platform/ILockedFramebuffer.cs | 9 +- .../Platform/IPlatformRenderInterface.cs | 32 ++-- .../Platform/LockedFramebuffer.cs | 10 +- .../Rendering/DeferredRenderer.cs | 8 +- .../Avalonia.Gtk3/ImageSurfaceFramebuffer.cs | 10 +- src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs | 18 +- .../LockedFramebuffer.cs | 9 +- .../Avalonia.MonoMac/EmulatedFramebuffer.cs | 12 +- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 6 +- .../Avalonia.Skia/FramebufferRenderTarget.cs | 2 +- src/Skia/Avalonia.Skia/ImmutableBitmap.cs | 27 ++- .../Avalonia.Skia/PlatformRenderInterface.cs | 30 ++- src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs | 18 +- src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 28 ++- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 20 +- .../FramebufferShimRenderTarget.cs | 4 +- .../Media/DrawingContextImpl.cs | 8 +- .../Media/ImageBrushImpl.cs | 4 +- .../Media/Imaging/BitmapImpl.cs | 4 +- .../Media/Imaging/D2DBitmapImpl.cs | 4 +- .../Imaging/D2DRenderTargetBitmapImpl.cs | 3 - .../Media/Imaging/WicBitmapImpl.cs | 38 ++-- .../Imaging/WicRenderTargetBitmapImpl.cs | 12 +- .../Media/Imaging/WriteableWicBitmapImpl.cs | 7 +- .../Avalonia.Direct2D1/PrimitiveExtensions.cs | 4 + .../Wpf/WritableBitmapSurface.cs | 3 +- .../Avalonia.Win32/FramebufferManager.cs | 4 +- .../Avalonia.Win32/WindowFramebuffer.cs | 28 ++- .../Avalonia.Controls.UnitTests/ImageTests.cs | 8 +- .../Avalonia.RenderTests/Media/BitmapTests.cs | 21 +-- tests/Avalonia.RenderTests/TestBase.cs | 11 +- .../MockPlatformRenderInterface.cs | 18 +- .../VisualTree/MockRenderInterface.cs | 6 +- 43 files changed, 436 insertions(+), 280 deletions(-) create mode 100644 src/Avalonia.Visuals/Media/PixelSize.cs diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 802b700a075..7fa68147d7d 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -57,7 +57,7 @@ public override void Render(DrawingContext context) if (source != null) { Rect viewPort = new Rect(Bounds.Size); - Size sourceSize = new Size(source.PixelWidth, source.PixelHeight); + Size sourceSize = new Size(source.PixelSize.Width, source.PixelSize.Height); Vector scale = Stretch.CalculateScaling(Bounds.Size, sourceSize); Size scaledSize = sourceSize * scale; Rect destRect = viewPort @@ -83,8 +83,7 @@ protected override Size MeasureOverride(Size availableSize) if (source != null) { - Size sourceSize = new Size(source.PixelWidth, source.PixelHeight); - + Size sourceSize = new Size(source.PixelSize.Width, source.PixelSize.Height); if (double.IsInfinity(availableSize.Width) || double.IsInfinity(availableSize.Height)) { return sourceSize; diff --git a/src/Avalonia.Controls/Remote/RemoteWidget.cs b/src/Avalonia.Controls/Remote/RemoteWidget.cs index 2d4f2e6b520..5a5ae97f4a5 100644 --- a/src/Avalonia.Controls/Remote/RemoteWidget.cs +++ b/src/Avalonia.Controls/Remote/RemoteWidget.cs @@ -59,9 +59,9 @@ public override void Render(DrawingContext context) if (_lastFrame != null) { var fmt = (PixelFormat) _lastFrame.Format; - if (_bitmap == null || _bitmap.PixelWidth != _lastFrame.Width || - _bitmap.PixelHeight != _lastFrame.Height) - _bitmap = new WriteableBitmap(_lastFrame.Width, _lastFrame.Height, fmt); + if (_bitmap == null || _bitmap.PixelSize.Width != _lastFrame.Width || + _bitmap.PixelSize.Height != _lastFrame.Height) + _bitmap = new WriteableBitmap(new PixelSize(_lastFrame.Width, _lastFrame.Height), new Vector(96, 96), fmt); using (var l = _bitmap.Lock()) { var lineLen = (fmt == PixelFormat.Rgb565 ? 2 : 4) * _lastFrame.Width; @@ -69,7 +69,7 @@ public override void Render(DrawingContext context) Marshal.Copy(_lastFrame.Data, y * _lastFrame.Stride, new IntPtr(l.Address.ToInt64() + l.RowBytes * y), lineLen); } - context.DrawImage(_bitmap, 1, new Rect(0, 0, _bitmap.PixelWidth, _bitmap.PixelHeight), + context.DrawImage(_bitmap, 1, new Rect(0, 0, _bitmap.PixelSize.Width, _bitmap.PixelSize.Height), new Rect(Bounds.Size)); } base.Render(context); diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index b302f2f5ecc..551428244a9 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -108,7 +108,7 @@ FrameMessage RenderFrame(int width, int height, ProtocolPixelFormat? format) var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { - _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), width, height, width * bpp, _dpi, (PixelFormat)fmt, + _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), new PixelSize(width, height), width * bpp, _dpi, (PixelFormat)fmt, null); Paint?.Invoke(new Rect(0, 0, width, height)); } diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index c34752a3efe..c88001cc0a1 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -1,6 +1,7 @@  netstandard2.0 + Avalonia diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs index cb98ed9a9cf..0ad826f5a93 100644 --- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs @@ -56,30 +56,29 @@ public virtual void Dispose() { PlatformImpl.Dispose(); } - + /// /// Initializes a new instance of the class. /// - /// Pixel format - /// Pointer to source bytes - /// Bitmap width - /// Bitmap height - /// Bytes per row - public Bitmap(PixelFormat format, IntPtr data, int width, int height, int stride) + /// The pixel format. + /// The pointer to the source bytes. + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. + /// The number of bytes per row. + public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) { PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService() - .LoadBitmap(format, data, width, height, stride)); + .LoadBitmap(format, data, size, dpi, stride)); } - /// - /// Gets the width of the bitmap, in pixels. - /// - public int PixelWidth => PlatformImpl.Item.PixelWidth; + /// + public Vector Dpi => PlatformImpl.Item.Dpi; - /// - /// Gets the height of the bitmap, in pixels. - /// - public int PixelHeight => PlatformImpl.Item.PixelHeight; + /// + public Size Size => PlatformImpl.Item.PixelSize.ToSize(Dpi); + + /// + public PixelSize PixelSize => PlatformImpl.Item.PixelSize; /// /// Gets the platform-specific bitmap implementation. diff --git a/src/Avalonia.Visuals/Media/Imaging/IBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/IBitmap.cs index 465173059e0..90b13088e17 100644 --- a/src/Avalonia.Visuals/Media/Imaging/IBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/IBitmap.cs @@ -14,20 +14,33 @@ namespace Avalonia.Media.Imaging public interface IBitmap : IDisposable { /// - /// Gets the width of the bitmap, in pixels. + /// Gets the dots per inch (DPI) of the image. /// - int PixelWidth { get; } + /// + /// Note that Skia does not currently support reading the DPI of an image so this value + /// will always be 96dpi on Skia. + /// + Vector Dpi { get; } /// - /// Gets the height of the bitmap, in pixels. + /// Gets the size of the bitmap, in device pixels. /// - int PixelHeight { get; } + PixelSize PixelSize { get; } /// /// Gets the platform-specific bitmap implementation. /// IRef PlatformImpl { get; } + /// + /// Gets the size of the image, in device independent pixels. + /// + /// + /// Note that Skia does not currently support reading the DPI of an image so this value + /// will equal on Skia. + /// + Size Size { get; } + /// /// Saves the bitmap to a file. /// diff --git a/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs index db2259dde30..d0b85ef14ee 100644 --- a/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs @@ -17,12 +17,19 @@ public class RenderTargetBitmap : Bitmap, IDisposable, IRenderTarget /// /// Initializes a new instance of the class. /// - /// The width of the bitmap in pixels. - /// The height of the bitmap in pixels. - /// The horizontal DPI of the bitmap. - /// The vertical DPI of the bitmap. - public RenderTargetBitmap(int pixelWidth, int pixelHeight, double dpiX = 96, double dpiY = 96) - : this(RefCountable.Create(CreateImpl(pixelWidth, pixelHeight, dpiX, dpiY))) + /// The size of the bitmap. + public RenderTargetBitmap(PixelSize pixelSize) + : this(pixelSize, new Vector(96, 96)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. + public RenderTargetBitmap(PixelSize pixelSize, Vector dpi) + : this(RefCountable.Create(CreateImpl(pixelSize, dpi))) { } @@ -45,15 +52,13 @@ private RenderTargetBitmap(IRef impl) : base(impl) /// /// Creates a platform-specific implementation for a . /// - /// The width of the bitmap. - /// The height of the bitmap. - /// The horizontal DPI of the bitmap. - /// The vertical DPI of the bitmap. + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. /// The platform-specific implementation. - private static IRenderTargetBitmapImpl CreateImpl(int width, int height, double dpiX, double dpiY) + private static IRenderTargetBitmapImpl CreateImpl(PixelSize size, Vector dpi) { IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService(); - return factory.CreateRenderTargetBitmap(width, height, dpiX, dpiY); + return factory.CreateRenderTargetBitmap(size, dpi); } /// diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs index 529c2efa429..88c0799cb90 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs @@ -7,8 +7,15 @@ namespace Avalonia.Media.Imaging /// public class WriteableBitmap : Bitmap { - public WriteableBitmap(int width, int height, PixelFormat? format = null) - : base(AvaloniaLocator.Current.GetService().CreateWriteableBitmap(width, height, format)) + /// + /// Initializes a new instance of the class. + /// + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. + /// The pixel format (optional). + /// An . + public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) + : base(AvaloniaLocator.Current.GetService().CreateWriteableBitmap(size, dpi, format)) { } diff --git a/src/Avalonia.Visuals/Media/PixelSize.cs b/src/Avalonia.Visuals/Media/PixelSize.cs new file mode 100644 index 00000000000..3d0ae0b351b --- /dev/null +++ b/src/Avalonia.Visuals/Media/PixelSize.cs @@ -0,0 +1,174 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Globalization; +using Avalonia.Utilities; + +namespace Avalonia +{ + /// + /// Represents a size in device pixels. + /// + public readonly struct PixelSize + { + /// + /// A size representing zero + /// + public static readonly PixelSize Empty = new PixelSize(0, 0); + + /// + /// Initializes a new instance of the structure. + /// + /// The width. + /// The height. + public PixelSize(int width, int height) + { + Width = width; + Height = height; + } + + /// + /// Gets the aspect ratio of the size. + /// + public double AspectRatio => (double)Width / Height; + + /// + /// Gets the width. + /// + public int Width { get; } + + /// + /// Gets the height. + /// + public int Height { get; } + + /// + /// Checks for equality between two s. + /// + /// The first size. + /// The second size. + /// True if the sizes are equal; otherwise false. + public static bool operator ==(PixelSize left, PixelSize right) + { + return left.Width == right.Width && left.Height == right.Height; + } + + /// + /// Checks for inequality between two s. + /// + /// The first size. + /// The second size. + /// True if the sizes are unequal; otherwise false. + public static bool operator !=(PixelSize left, PixelSize right) + { + return !(left == right); + } + + /// + /// Parses a string. + /// + /// The string. + /// The . + public static PixelSize Parse(string s) + { + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Size")) + { + return new PixelSize( + tokenizer.ReadInt32(), + tokenizer.ReadInt32()); + } + } + + /// + /// Checks for equality between a size and an object. + /// + /// The object. + /// + /// True if is a size that equals the current size. + /// + public override bool Equals(object obj) + { + if (obj is PixelSize other) + { + return this == other; + } + + return false; + } + + /// + /// Returns a hash code for a . + /// + /// The hash code. + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = (hash * 23) + Width.GetHashCode(); + hash = (hash * 23) + Height.GetHashCode(); + return hash; + } + } + + /// + /// Returns a new with the same height and the specified width. + /// + /// The width. + /// The new . + public PixelSize WithWidth(int width) => new PixelSize(width, Height); + + /// + /// Returns a new with the same width and the specified height. + /// + /// The height. + /// The new . + public PixelSize WithHeight(int height) => new PixelSize(Width, height); + + /// + /// Converts the to a device-independent using the + /// specified dots per inch (DPI). + /// + /// The dots per inch. + /// The device-independent size. + public Size ToSize(double dpi) => new Size(Width / (dpi / 96), Height / (dpi / 96)); + + /// + /// Converts the to a device-independent using the + /// specified dots per inch (DPI). + /// + /// The dots per inch. + /// The device-independent size. + public Size ToSize(Vector dpi) => new Size(Width / (dpi.X / 96), Height / (dpi.Y / 96)); + + /// + /// Converts a to device pixels using the specified dots per inch (DPI). + /// + /// The size. + /// The dots per inch. + /// The device-independent size. + public static PixelSize FromSize(Size size, double dpi) => new PixelSize( + (int)(size.Width * (dpi / 96)), + (int)(size.Height * (dpi / 96))); + + /// + /// Converts a to device pixels using the specified dots per inch (DPI). + /// + /// The size. + /// The dots per inch. + /// The device-independent size. + public static PixelSize FromSize(Size size, Vector dpi) => new PixelSize( + (int)(size.Width * (dpi.X / 96)), + (int)(size.Height * (dpi.Y / 96))); + + /// + /// Returns the string representation of the size. + /// + /// The string representation of the size. + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", Width, Height); + } + } +} diff --git a/src/Avalonia.Visuals/Platform/IBitmapImpl.cs b/src/Avalonia.Visuals/Platform/IBitmapImpl.cs index d60838452e4..f9608f9ea05 100644 --- a/src/Avalonia.Visuals/Platform/IBitmapImpl.cs +++ b/src/Avalonia.Visuals/Platform/IBitmapImpl.cs @@ -12,14 +12,14 @@ namespace Avalonia.Platform public interface IBitmapImpl : IDisposable { /// - /// Gets the width of the bitmap, in pixels. + /// Gets the dots per inch (DPI) of the image. /// - int PixelWidth { get; } + Vector Dpi { get; } /// - /// Gets the height of the bitmap, in pixels. + /// Gets the size of the bitmap, in device pixels. /// - int PixelHeight { get; } + PixelSize PixelSize { get; } /// /// Saves the bitmap to a file. diff --git a/src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs b/src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs index 45ca1a5a998..2f631142d2f 100644 --- a/src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs +++ b/src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs @@ -10,14 +10,9 @@ public interface ILockedFramebuffer : IDisposable IntPtr Address { get; } /// - /// Framebuffer width + /// Gets the framebuffer size in device pixels. /// - int Width { get; } - - /// - /// Framebuffer height - /// - int Height { get; } + PixelSize Size{ get; } /// /// Number of bytes per row diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index cc17efd2bbe..aacdef05385 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -49,25 +49,19 @@ IFormattedTextImpl CreateFormattedText( /// /// Creates a render target bitmap implementation. /// - /// The width of the bitmap. - /// The height of the bitmap. - /// The horizontal DPI of the bitmap. - /// The vertical DPI of the bitmap. + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. /// An . - IRenderTargetBitmapImpl CreateRenderTargetBitmap( - int width, - int height, - double dpiX, - double dpiY); + IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi); /// /// Creates a writeable bitmap implementation. /// - /// The width of the bitmap. - /// The height of the bitmap. + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. /// Pixel format (optional). /// An . - IWriteableBitmapImpl CreateWriteableBitmap(int width, int height, PixelFormat? format = null); + IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null); /// /// Loads a bitmap implementation from a file.. @@ -84,14 +78,14 @@ IRenderTargetBitmapImpl CreateRenderTargetBitmap( IBitmapImpl LoadBitmap(Stream stream); /// - /// Loads a bitmap implementation from a pixels in memory.. + /// Loads a bitmap implementation from a pixels in memory. /// - /// Pixel format - /// Pointer to source bytes - /// Bitmap width - /// Bitmap height - /// Bytes per row + /// The pixel format. + /// The pointer to source bytes. + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. + /// The number of bytes per row. /// An . - IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, int width, int height, int stride); + IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride); } } diff --git a/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs b/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs index c03b714956a..c5b3c048b11 100644 --- a/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs +++ b/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs @@ -6,21 +6,19 @@ public class LockedFramebuffer : ILockedFramebuffer { private readonly Action _onDispose; - public LockedFramebuffer(IntPtr address, int width, int height, int rowBytes, Vector dpi, PixelFormat format, + public LockedFramebuffer(IntPtr address, PixelSize size, int rowBytes, Vector dpi, PixelFormat format, Action onDispose) { _onDispose = onDispose; Address = address; - Width = width; - Height = height; + Size = Size; RowBytes = rowBytes; Dpi = dpi; Format = format; } public IntPtr Address { get; } - public int Width { get; } - public int Height { get; } + public PixelSize Size { get; } public int RowBytes { get; } public Vector Dpi { get; } public PixelFormat Format { get; } @@ -30,4 +28,4 @@ public void Dispose() _onDispose?.Invoke(); } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index dc1d2933d06..52172f654ed 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -344,7 +344,7 @@ private void RenderComposite(Scene scene, IDrawingContextImpl context) foreach (var layer in scene.Layers) { var bitmap = Layers[layer.LayerRoot].Bitmap; - var sourceRect = new Rect(0, 0, bitmap.Item.PixelWidth, bitmap.Item.PixelHeight); + var sourceRect = new Rect(0, 0, bitmap.Item.PixelSize.Width, bitmap.Item.PixelSize.Height); if (layer.GeometryClip != null) { @@ -368,7 +368,7 @@ private void RenderComposite(Scene scene, IDrawingContextImpl context) if (_overlay != null) { - var sourceRect = new Rect(0, 0, _overlay.Item.PixelWidth, _overlay.Item.PixelHeight); + var sourceRect = new Rect(0, 0, _overlay.Item.PixelSize.Width, _overlay.Item.PixelSize.Height); context.DrawImage(_overlay, 0.5, sourceRect, clientRect); } @@ -453,8 +453,8 @@ private IRef GetOverlay( var pixelSize = size * scaling; if (_overlay == null || - _overlay.Item.PixelWidth != pixelSize.Width || - _overlay.Item.PixelHeight != pixelSize.Height) + _overlay.Item.PixelSize.Width != pixelSize.Width || + _overlay.Item.PixelSize.Height != pixelSize.Height) { _overlay?.Dispose(); _overlay = RefCountable.Create(parentContext.CreateLayer(size)); diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs index cd0074c2872..44d887241ca 100644 --- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs +++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs @@ -22,8 +22,7 @@ public ImageSurfaceFramebuffer(WindowBaseImpl impl, int width, int height, int f height *= _factor; _surface = new ManagedCairoSurface(width, height); - Width = width; - Height = height; + Size = new PixelSize(width, height); Address = _surface.Buffer; RowBytes = _surface.Stride; Native.CairoSurfaceFlush(_surface.Surface); @@ -115,18 +114,17 @@ public void Dispose() if (_impl.CurrentCairoContext != IntPtr.Zero) Draw(_impl.CurrentCairoContext, _surface.Surface, _factor); else - DrawToWidget(_widget, _surface.Surface, Width, Height, _factor); + DrawToWidget(_widget, _surface.Surface, Size.Width, Size.Height, _factor); _surface.Dispose(); } else - _impl.SetNextRenderOperation(new RenderOp(_widget, _surface, _factor, Width, Height)); + _impl.SetNextRenderOperation(new RenderOp(_widget, _surface, _factor, Size.Width, Size.Height)); _surface = null; } } public IntPtr Address { get; } - public int Width { get; } - public int Height { get; } + public PixelSize Size { get; } public int RowBytes { get; } diff --git a/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs b/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs index dde29f85c2c..c84d7c75417 100644 --- a/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs +++ b/src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs @@ -13,12 +13,11 @@ public X11Framebuffer(IntPtr display, IntPtr xid, int width, int height, int fac { _display = display; _xid = xid; - Width = width*factor; - Height = height*factor; - RowBytes = Width * 4; + Size = new PixelSize(width * factor, height * factor); + RowBytes = Size.Width * 4; Dpi = new Vector(96, 96) * factor; Format = PixelFormat.Bgra8888; - _blob = AvaloniaLocator.Current.GetService().AllocBlob(RowBytes * Height); + _blob = AvaloniaLocator.Current.GetService().AllocBlob(RowBytes * Size.Height); Address = _blob.Address; } @@ -26,8 +25,8 @@ public void Dispose() { var image = new X11.XImage(); int bitsPerPixel = 32; - image.width = Width; - image.height = Height; + image.width = Size.Width; + image.height = Size.Height; image.format = 2; //ZPixmap; image.data = Address; image.byte_order = 0;// LSBFirst; @@ -35,12 +34,12 @@ public void Dispose() image.bitmap_bit_order = 0;// LSBFirst; image.bitmap_pad = bitsPerPixel; image.depth = 24; - image.bytes_per_line = RowBytes - Width * 4; + image.bytes_per_line = RowBytes - Size.Width * 4; image.bits_per_pixel = bitsPerPixel; X11.XLockDisplay(_display); X11.XInitImage(ref image); var gc = X11.XCreateGC(_display, _xid, 0, IntPtr.Zero); - X11.XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint) Width, (uint) Height); + X11.XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint)Size.Width, (uint)Size.Height); X11.XFreeGC(_display, gc); X11.XSync(_display, true); X11.XUnlockDisplay(_display); @@ -48,8 +47,7 @@ public void Dispose() } public IntPtr Address { get; } - public int Width { get; } - public int Height { get; } + public PixelSize Size { get; } public int RowBytes { get; } public Vector Dpi { get; } public PixelFormat Format { get; } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs index 795d9648ea6..ed59166eb88 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs @@ -19,7 +19,7 @@ public LockedFramebuffer(int fb, fb_fix_screeninfo fixedInfo, fb_var_screeninfo _address = address; Dpi = dpi; //Use double buffering to avoid flicker - Address = Marshal.AllocHGlobal(RowBytes * Height); + Address = Marshal.AllocHGlobal(RowBytes * Size.Height); } @@ -31,17 +31,16 @@ void VSync() public void Dispose() { VSync(); - NativeUnsafeMethods.memcpy(_address, Address, new IntPtr(RowBytes * Height)); + NativeUnsafeMethods.memcpy(_address, Address, new IntPtr(RowBytes * Size.Height)); Marshal.FreeHGlobal(Address); Address = IntPtr.Zero; } public IntPtr Address { get; private set; } - public int Width => (int)_varInfo.xres; - public int Height => (int) _varInfo.yres; + public PixelSize Size => new PixelSize((int)_varInfo.xres, (int) _varInfo.yres); public int RowBytes => (int) _fixedInfo.line_length; public Vector Dpi { get; } public PixelFormat Format => _varInfo.blue.offset == 16 ? PixelFormat.Rgba8888 : PixelFormat.Bgra8888; } -} \ No newline at end of file +} diff --git a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs index 4da67ae1ddd..8838c43201f 100644 --- a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs +++ b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs @@ -24,12 +24,11 @@ public EmulatedFramebuffer(TopLevelImpl.TopLevelView view) _isDeferred = !Dispatcher.UIThread.CheckAccess(); _logicalSize = _view.LogicalSize; var pixelSize = _view.PixelSize; - Width = (int)pixelSize.Width; - Height = (int)pixelSize.Height; - RowBytes = Width * 4; + Size = new PixelSize((int)pixelSize.Width, (int)pixelSize.Height); + RowBytes = Size.Width * 4; Dpi = new Vector(96 * pixelSize.Width / _logicalSize.Width, 96 * pixelSize.Height / _logicalSize.Height); Format = PixelFormat.Rgba8888; - var size = Height * RowBytes; + var size = Size.Height * RowBytes; _blob = AvaloniaLocator.Current.GetService().AllocBlob(size); memset(Address, 0, new IntPtr(size)); } @@ -43,7 +42,7 @@ public void Dispose() try { using (var colorSpace = CGColorSpace.CreateDeviceRGB()) - using (var bContext = new CGBitmapContext(Address, Width, Height, 8, Width * 4, + using (var bContext = new CGBitmapContext(Address, Size.Width, Size.Height, 8, Size.Width * 4, colorSpace, (CGImageAlphaInfo)nfo)) image = bContext.ToImage(); lock (_view.SyncRoot) @@ -79,8 +78,7 @@ public void Dispose() } public IntPtr Address => _blob.Address; - public int Width { get; } - public int Height { get; } + public PixelSize Size { get; } public int RowBytes { get; } public Vector Dpi { get; } public PixelFormat Format { get; } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 685987ae800..c8fb8f40330 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -135,7 +135,7 @@ private static SKFilterQuality GetInterpolationMode(BitmapInterpolationMode inte public void DrawImage(IRef source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect) { PushOpacityMask(opacityMask, opacityMaskRect); - DrawImage(source, 1, new Rect(0, 0, source.Item.PixelWidth, source.Item.PixelHeight), destRect, BitmapInterpolationMode.Default); + DrawImage(source, 1, new Rect(0, 0, source.Item.PixelSize.Width, source.Item.PixelSize.Height), destRect, BitmapInterpolationMode.Default); PopOpacityMask(); } @@ -384,7 +384,7 @@ private void ConfigureGradientBrush(ref PaintWrapper paintWrapper, Size targetSi private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage) { var calc = new TileBrushCalculator(tileBrush, - new Size(tileBrushImage.PixelWidth, tileBrushImage.PixelHeight), targetSize); + new Size(tileBrushImage.PixelSize.Width, tileBrushImage.PixelSize.Height), targetSize); var intermediate = CreateRenderTarget( (int)calc.IntermediateSize.Width, @@ -394,7 +394,7 @@ private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, using (var context = intermediate.CreateDrawingContext(null)) { - var rect = new Rect(0, 0, tileBrushImage.PixelWidth, tileBrushImage.PixelHeight); + var rect = new Rect(0, 0, tileBrushImage.PixelSize.Width, tileBrushImage.PixelSize.Height); context.Clear(Colors.Transparent); context.PushClip(calc.IntermediateClip); diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs index 829b9097c97..b2c936d6ad6 100644 --- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs @@ -41,7 +41,7 @@ public void Dispose() public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) { var framebuffer = _platformSurface.Lock(); - var framebufferImageInfo = new SKImageInfo(framebuffer.Width, framebuffer.Height, + var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height, framebuffer.Format.ToSkColorType(), SKAlphaType.Premul); CreateSurface(framebufferImageInfo, framebuffer); diff --git a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs index 332b8547bf3..ee3a3b1a32a 100644 --- a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs +++ b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs @@ -31,22 +31,24 @@ public ImmutableBitmap(Stream stream) throw new ArgumentException("Unable to load bitmap from provided data"); } - PixelWidth = _image.Width; - PixelHeight = _image.Height; + PixelSize = new PixelSize(_image.Width, _image.Height); + + // TODO: Skia doesn't have an API for DPI. + Dpi = new Vector(96, 96); } } /// /// Create immutable bitmap from given pixel data copy. /// - /// Width of data pixels. - /// Height of data pixels. + /// Size of the bitmap. + /// DPI of the bitmap. /// Stride of data pixels. /// Format of data pixels. /// Data pixels. - public ImmutableBitmap(int width, int height, int stride, PixelFormat format, IntPtr data) + public ImmutableBitmap(PixelSize size, Vector dpi, int stride, PixelFormat format, IntPtr data) { - var imageInfo = new SKImageInfo(width, height, format.ToSkColorType(), SKAlphaType.Premul); + var imageInfo = new SKImageInfo(size.Width, size.Height, format.ToSkColorType(), SKAlphaType.Premul); _image = SKImage.FromPixelCopy(imageInfo, data, stride); @@ -55,15 +57,12 @@ public ImmutableBitmap(int width, int height, int stride, PixelFormat format, In throw new ArgumentException("Unable to create bitmap from provided data"); } - PixelWidth = width; - PixelHeight = height; + PixelSize = size; + Dpi = dpi; } - /// - public int PixelWidth { get; } - - /// - public int PixelHeight { get; } + public Vector Dpi { get; } + public PixelSize PixelSize { get; } /// public void Dispose() @@ -89,4 +88,4 @@ public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, context.Canvas.DrawImage(_image, sourceRect, destRect, paint); } } -} \ No newline at end of file +} diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index d4e6403dc94..b6a674271c8 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -49,34 +49,28 @@ public IBitmapImpl LoadBitmap(string fileName) } /// - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, int width, int height, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) { - return new ImmutableBitmap(width, height, stride, format, data); + return new ImmutableBitmap(size, dpi, stride, format, data); } /// - public IRenderTargetBitmapImpl CreateRenderTargetBitmap( - int width, - int height, - double dpiX, - double dpiY) + public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi) { - if (width < 1) + if (size.Width < 1) { - throw new ArgumentException("Width can't be less than 1", nameof(width)); + throw new ArgumentException("Width can't be less than 1", nameof(size)); } - if (height < 1) + if (size.Height < 1) { - throw new ArgumentException("Height can't be less than 1", nameof(height)); + throw new ArgumentException("Height can't be less than 1", nameof(size)); } - var dpi = new Vector(dpiX, dpiY); - var createInfo = new SurfaceRenderTarget.CreateInfo { - Width = width, - Height = height, + Width = size.Width, + Height = size.Height, Dpi = dpi, DisableTextLcdRendering = false }; @@ -100,9 +94,9 @@ public virtual IRenderTarget CreateRenderTarget(IEnumerable surfaces) } /// - public IWriteableBitmapImpl CreateWriteableBitmap(int width, int height, PixelFormat? format = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) { - return new WriteableBitmapImpl(width, height, format); + return new WriteableBitmapImpl(size, dpi, format); } } -} \ No newline at end of file +} diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs index 88200dcfbe8..e84c5127960 100644 --- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs @@ -15,7 +15,6 @@ namespace Avalonia.Skia /// public class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl { - private readonly Vector _dpi; private readonly SKSurface _surface; private readonly SKCanvas _canvas; private readonly bool _disableLcdRendering; @@ -26,12 +25,11 @@ public class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl /// Create info. public SurfaceRenderTarget(CreateInfo createInfo) { - PixelWidth = createInfo.Width; - PixelHeight = createInfo.Height; - _dpi = createInfo.Dpi; - _disableLcdRendering = createInfo.DisableTextLcdRendering; + PixelSize = new PixelSize(createInfo.Width, createInfo.Height); + Dpi = createInfo.Dpi; - _surface = CreateSurface(PixelWidth, PixelHeight, createInfo.Format); + _disableLcdRendering = createInfo.DisableTextLcdRendering; + _surface = CreateSurface(PixelSize.Width, PixelSize.Height, createInfo.Format); _canvas = _surface?.Canvas; @@ -71,7 +69,7 @@ public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrush var createInfo = new DrawingContextImpl.CreateInfo { Canvas = _canvas, - Dpi = _dpi, + Dpi = Dpi, VisualBrushRenderer = visualBrushRenderer, DisableTextLcdRendering = _disableLcdRendering }; @@ -80,10 +78,10 @@ public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrush } /// - public int PixelWidth { get; } + public Vector Dpi { get; } /// - public int PixelHeight { get; } + public PixelSize PixelSize { get; } /// public void Save(string fileName) @@ -166,4 +164,4 @@ public struct CreateInfo public bool DisableTextLcdRendering; } } -} \ No newline at end of file +} diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index ab6c399ff41..3abff100987 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -20,13 +20,13 @@ public class WriteableBitmapImpl : IWriteableBitmapImpl, IDrawableBitmapImpl /// /// Create new writeable bitmap. /// - /// Width. - /// Height. - /// Format. - public WriteableBitmapImpl(int width, int height, PixelFormat? format = null) + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. + /// The pixel format. + public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat? format = null) { - PixelHeight = height; - PixelWidth = width; + PixelSize = size; + Dpi = dpi; var colorType = PixelFormatHelper.ResolveColorType(format); @@ -36,24 +36,23 @@ public WriteableBitmapImpl(int width, int height, PixelFormat? format = null) { _bitmap = new SKBitmap(); - var nfo = new SKImageInfo(width, height, colorType, SKAlphaType.Premul); + var nfo = new SKImageInfo(size.Width, size.Height, colorType, SKAlphaType.Premul); var blob = runtimePlatform.AllocBlob(nfo.BytesSize); _bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, null, s_releaseDelegate, blob); } else { - _bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul); + _bitmap = new SKBitmap(size.Width, size.Height, colorType, SKAlphaType.Premul); } _bitmap.Erase(SKColor.Empty); } - /// - public int PixelWidth { get; } + public Vector Dpi { get; } /// - public int PixelHeight { get; } + public PixelSize PixelSize { get; } /// public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint) @@ -133,10 +132,7 @@ public void Dispose() public IntPtr Address => _bitmap.GetPixels(); /// - public int Width => _bitmap.Width; - - /// - public int Height => _bitmap.Height; + public PixelSize Size => new PixelSize(_bitmap.Width, _bitmap.Height); /// public int RowBytes => _bitmap.RowBytes; @@ -148,4 +144,4 @@ public void Dispose() public PixelFormat Format => _bitmap.ColorType.ToPixelFormat(); } } -} \ No newline at end of file +} diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index c330377e20c..3ec18dac5e9 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -110,9 +110,9 @@ public static void Initialize() SharpDX.Configuration.EnableReleaseOnFinalizer = true; } - public IBitmapImpl CreateBitmap(int width, int height) + public IBitmapImpl CreateBitmap(PixelSize size, Vector dpi) { - return new WicBitmapImpl(width, height); + return new WicBitmapImpl(size, dpi); } public IFormattedTextImpl CreateFormattedText( @@ -159,18 +159,14 @@ public IRenderTarget CreateRenderTarget(IEnumerable surfaces) throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from any of provided surfaces"); } - public IRenderTargetBitmapImpl CreateRenderTargetBitmap( - int width, - int height, - double dpiX, - double dpiY) + public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi) { - return new WicRenderTargetBitmapImpl(width, height, dpiX, dpiY); + return new WicRenderTargetBitmapImpl(size, dpi); } - public IWriteableBitmapImpl CreateWriteableBitmap(int width, int height, PixelFormat? format = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) { - return new WriteableWicBitmapImpl(width, height, format); + return new WriteableWicBitmapImpl(size, dpi, format); } public IStreamGeometryImpl CreateStreamGeometry() @@ -188,9 +184,9 @@ public IBitmapImpl LoadBitmap(Stream stream) return new WicBitmapImpl(stream); } - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, int width, int height, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) { - return new WicBitmapImpl(format, data, width, height, stride); + return new WicBitmapImpl(format, data, size, dpi, stride); } } } diff --git a/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs index 5ae174083c7..f7e7ed3dc22 100644 --- a/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs +++ b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs @@ -40,7 +40,7 @@ class FramebufferShim : WicRenderTargetBitmapImpl private readonly ILockedFramebuffer _target; public FramebufferShim(ILockedFramebuffer target) : - base(target.Width, target.Height, target.Dpi.X, target.Dpi.Y, target.Format) + base(target.Size, target.Dpi, target.Format) { _target = target; } @@ -51,7 +51,7 @@ public override IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer vi { using (var l = WicImpl.Lock(BitmapLockFlags.Read)) { - for (var y = 0; y < _target.Height; y++) + for (var y = 0; y < _target.Size.Height; y++) { UnmanagedMethods.CopyMemory( (_target.Address + _target.RowBytes * y), diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index b842f26edb4..efe26ba01a4 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -330,12 +330,8 @@ public IRenderTargetBitmapImpl CreateLayer(Size size) { var platform = AvaloniaLocator.Current.GetService(); var dpi = new Vector(_deviceContext.DotsPerInch.Width, _deviceContext.DotsPerInch.Height); - var pixelSize = size * (dpi / 96); - return platform.CreateRenderTargetBitmap( - (int)pixelSize.Width, - (int)pixelSize.Height, - dpi.X, - dpi.Y); + var pixelSize = PixelSize.FromSize(size, dpi); + return platform.CreateRenderTargetBitmap(pixelSize, dpi); } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 3fc8760a9e7..55877cefcbf 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -20,7 +20,7 @@ public ImageBrushImpl( BitmapImpl bitmap, Size targetSize) { - var calc = new TileBrushCalculator(brush, new Size(bitmap.PixelWidth, bitmap.PixelHeight), targetSize); + var calc = new TileBrushCalculator(brush, bitmap.PixelSize.ToSize(96), targetSize); if (!calc.NeedsIntermediate) { @@ -99,7 +99,7 @@ private BitmapRenderTarget RenderIntermediate( using (var context = new RenderTarget(result).CreateDrawingContext(null)) { - var rect = new Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight); + var rect = new Rect(0, 0, bitmap.PixelSize.Width, bitmap.PixelSize.Height); context.Clear(Colors.Transparent); context.PushClip(calc.IntermediateClip); diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs index 30af01283ae..f3879b4eff7 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs @@ -7,8 +7,8 @@ namespace Avalonia.Direct2D1.Media { public abstract class BitmapImpl : IBitmapImpl, IDisposable { - public abstract int PixelWidth { get; } - public abstract int PixelHeight { get; } + public abstract Vector Dpi { get; } + public abstract PixelSize PixelSize { get; } public abstract OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target); diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs index b0edb9c3bf6..1ee869ecb90 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs @@ -30,8 +30,8 @@ public D2DBitmapImpl(Bitmap d2DBitmap) _direct2DBitmap = d2DBitmap ?? throw new ArgumentNullException(nameof(d2DBitmap)); } - public override int PixelWidth => _direct2DBitmap.PixelSize.Width; - public override int PixelHeight => _direct2DBitmap.PixelSize.Height; + public override Vector Dpi => _direct2DBitmap.DotsPerInch.ToAvaloniaVector(); + public override PixelSize PixelSize => _direct2DBitmap.PixelSize.ToAvalonia(); public override void Dispose() { diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs index 3646d9e9e12..88602b935ea 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs @@ -19,9 +19,6 @@ public D2DRenderTargetBitmapImpl(BitmapRenderTarget renderTarget) _renderTarget = renderTarget; } - public override int PixelWidth => _renderTarget.PixelSize.Width; - public override int PixelHeight => _renderTarget.PixelSize.Height; - public static D2DRenderTargetBitmapImpl CreateCompatible( SharpDX.Direct2D1.RenderTarget renderTarget, Size size) diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs index b16ea45fc6f..89b96953ade 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs @@ -42,10 +42,10 @@ public WicBitmapImpl(Stream stream) /// /// Initializes a new instance of the class. /// - /// The width of the bitmap. - /// The height of the bitmap. + /// The size of the bitmap in device pixels. + /// The DPI of the bitmap. /// Pixel format - public WicBitmapImpl(int width, int height, APixelFormat? pixelFormat = null) + public WicBitmapImpl(PixelSize size, Vector dpi, APixelFormat? pixelFormat = null) { if (!pixelFormat.HasValue) { @@ -55,19 +55,22 @@ public WicBitmapImpl(int width, int height, APixelFormat? pixelFormat = null) PixelFormat = pixelFormat; WicImpl = new Bitmap( Direct2D1Platform.ImagingFactory, - width, - height, + size.Width, + size.Height, pixelFormat.Value.ToWic(), BitmapCreateCacheOption.CacheOnLoad); + WicImpl.SetResolution(dpi.X, dpi.Y); } - public WicBitmapImpl(APixelFormat format, IntPtr data, int width, int height, int stride) + public WicBitmapImpl(APixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) { - WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, width, height, format.ToWic(), BitmapCreateCacheOption.CacheOnDemand); + WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, size.Width, size.Height, format.ToWic(), BitmapCreateCacheOption.CacheOnDemand); + WicImpl.SetResolution(dpi.X, dpi.Y); + PixelFormat = format; using (var l = WicImpl.Lock(BitmapLockFlags.Write)) { - for (var row = 0; row < height; row++) + for (var row = 0; row < size.Height; row++) { UnmanagedMethods.CopyMemory( (l.Data.DataPointer + row * l.Stride), @@ -77,17 +80,18 @@ public WicBitmapImpl(APixelFormat format, IntPtr data, int width, int height, in } } - protected APixelFormat? PixelFormat { get; } + public override Vector Dpi + { + get + { + WicImpl.GetResolution(out double x, out double y); + return new Vector(x, y); + } + } - /// - /// Gets the width of the bitmap, in pixels. - /// - public override int PixelWidth => WicImpl.Size.Width; + public override PixelSize PixelSize => WicImpl.Size.ToAvalonia(); - /// - /// Gets the height of the bitmap, in pixels. - /// - public override int PixelHeight => WicImpl.Size.Height; + protected APixelFormat? PixelFormat { get; } public override void Dispose() { diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs index aa8b3ead420..ca441e7b690 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs @@ -13,17 +13,15 @@ public class WicRenderTargetBitmapImpl : WicBitmapImpl, IRenderTargetBitmapImpl private readonly WicRenderTarget _renderTarget; public WicRenderTargetBitmapImpl( - int width, - int height, - double dpiX, - double dpiY, + PixelSize size, + Vector dpi, Platform.PixelFormat? pixelFormat = null) - : base(width, height, pixelFormat) + : base(size, dpi, pixelFormat) { var props = new RenderTargetProperties { - DpiX = (float)dpiX, - DpiY = (float)dpiY, + DpiX = (float)dpi.X, + DpiY = (float)dpi.Y, }; _renderTarget = new WicRenderTarget( diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs index 5ca78ef2780..e3bd3b3218f 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs @@ -10,8 +10,8 @@ namespace Avalonia.Direct2D1.Media.Imaging { class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl { - public WriteableWicBitmapImpl(int width, int height, PixelFormat? pixelFormat) - : base(width, height, pixelFormat) + public WriteableWicBitmapImpl(PixelSize size, Vector dpi, PixelFormat? pixelFormat) + : base(size, dpi, pixelFormat) { } @@ -33,8 +33,7 @@ public void Dispose() } public IntPtr Address => _lock.Data.DataPointer; - public int Width => _lock.Size.Width; - public int Height => _lock.Size.Height; + public PixelSize Size => _lock.Size.ToAvalonia(); public int RowBytes => _lock.Stride; public Vector Dpi { get; } = new Vector(96, 96); public PixelFormat Format => _format; diff --git a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs index 124e33c5a3b..52090142718 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -41,6 +41,10 @@ public static Rect ToAvalonia(this RawRectangleF r) return new Rect(new Point(r.Left, r.Top), new Point(r.Right, r.Bottom)); } + public static PixelSize ToAvalonia(this Size2 p) => new PixelSize(p.Width, p.Height); + + public static Vector ToAvaloniaVector(this Size2F p) => new Vector(p.Width, p.Height); + public static RawRectangleF ToSharpDX(this Rect r) { return new RawRectangleF((float)r.X, (float)r.Y, (float)r.Right, (float)r.Bottom); diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs index 79340fa335c..05fa9d94267 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs @@ -58,8 +58,7 @@ public void Dispose() } public IntPtr Address => _bitmap.BackBuffer; - public int Width => _bitmap.PixelWidth; - public int Height => _bitmap.PixelHeight; + public PixelSize Size => new PixelSize(_bitmap.PixelWidth, _bitmap.PixelHeight); public int RowBytes => _bitmap.BackBufferStride; public Vector Dpi { get; } public PixelFormat Format => PixelFormat.Bgra8888; diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs index bb0bde34ff1..c9107031816 100644 --- a/src/Windows/Avalonia.Win32/FramebufferManager.cs +++ b/src/Windows/Avalonia.Win32/FramebufferManager.cs @@ -21,11 +21,11 @@ public ILockedFramebuffer Lock() UnmanagedMethods.GetClientRect(_hwnd, out rc); var width = rc.right - rc.left; var height = rc.bottom - rc.top; - if ((_fb == null || _fb.Width != width || _fb.Height != height) && width > 0 && height > 0) + if ((_fb == null || _fb.Size.Width != width || _fb.Size.Height != height) && width > 0 && height > 0) { _fb?.Deallocate(); _fb = null; - _fb = new WindowFramebuffer(_hwnd, width, height); + _fb = new WindowFramebuffer(_hwnd, new PixelSize(width, height)); } return _fb; } diff --git a/src/Windows/Avalonia.Win32/WindowFramebuffer.cs b/src/Windows/Avalonia.Win32/WindowFramebuffer.cs index 6193ebd02bb..9c331f662dc 100644 --- a/src/Windows/Avalonia.Win32/WindowFramebuffer.cs +++ b/src/Windows/Avalonia.Win32/WindowFramebuffer.cs @@ -11,21 +11,21 @@ public class WindowFramebuffer : ILockedFramebuffer private IUnmanagedBlob _bitmapBlob; private UnmanagedMethods.BITMAPINFOHEADER _bmpInfo; - public WindowFramebuffer(IntPtr handle, int width, int height) + public WindowFramebuffer(IntPtr handle, PixelSize size) { - if (width <= 0) - throw new ArgumentException("width is less than zero"); - if (height <= 0) - throw new ArgumentException("height is less than zero"); + if (size.Width <= 0) + throw new ArgumentException("Width is less than zero"); + if (size.Height <= 0) + throw new ArgumentException("Height is less than zero"); _handle = handle; _bmpInfo.Init(); _bmpInfo.biPlanes = 1; _bmpInfo.biBitCount = 32; _bmpInfo.Init(); - _bmpInfo.biWidth = width; - _bmpInfo.biHeight = -height; - _bitmapBlob = AvaloniaLocator.Current.GetService().AllocBlob(width * height * 4); + _bmpInfo.biWidth = size.Width; + _bmpInfo.biHeight = -size.Height; + _bitmapBlob = AvaloniaLocator.Current.GetService().AllocBlob(size.Width * size.Height * 4); } ~WindowFramebuffer() @@ -34,7 +34,7 @@ public WindowFramebuffer(IntPtr handle, int width, int height) } public IntPtr Address => _bitmapBlob.Address; - public int RowBytes => Width * 4; + public int RowBytes => Size.Width * 4; public PixelFormat Format => PixelFormat.Bgra8888; public Vector Dpi @@ -61,19 +61,17 @@ public Vector Dpi } } - public int Width => _bmpInfo.biWidth; - - public int Height => -_bmpInfo.biHeight; + public PixelSize Size => new PixelSize(_bmpInfo.biWidth, _bmpInfo.biHeight); public void DrawToDevice(IntPtr hDC, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1, int height = -1) { if (width == -1) - width = Width; + width = Size.Width; if (height == -1) - height = Height; + height = Size.Height; UnmanagedMethods.SetDIBitsToDevice(hDC, destX, destY, (uint) width, (uint) height, srcX, srcY, - 0, (uint)Height, _bitmapBlob.Address, ref _bmpInfo, 0); + 0, (uint)Size.Height, _bitmapBlob.Address, ref _bmpInfo, 0); } public bool DrawToWindow(IntPtr hWnd, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1, diff --git a/tests/Avalonia.Controls.UnitTests/ImageTests.cs b/tests/Avalonia.Controls.UnitTests/ImageTests.cs index 7f2b5e77f99..7a0e5681d7e 100644 --- a/tests/Avalonia.Controls.UnitTests/ImageTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ImageTests.cs @@ -13,7 +13,7 @@ public class ImageTests [Fact] public void Measure_Should_Return_Correct_Size_For_No_Stretch() { - var bitmap = Mock.Of(x => x.PixelWidth == 50 && x.PixelHeight == 100); + var bitmap = Mock.Of(x => x.Size == new Size(50, 100)); var target = new Image(); target.Stretch = Stretch.None; target.Source = bitmap; @@ -26,7 +26,7 @@ public void Measure_Should_Return_Correct_Size_For_No_Stretch() [Fact] public void Measure_Should_Return_Correct_Size_For_Fill_Stretch() { - var bitmap = Mock.Of(x => x.PixelWidth == 50 && x.PixelHeight == 100); + var bitmap = Mock.Of(x => x.Size == new Size(50, 100)); var target = new Image(); target.Stretch = Stretch.Fill; target.Source = bitmap; @@ -39,7 +39,7 @@ public void Measure_Should_Return_Correct_Size_For_Fill_Stretch() [Fact] public void Measure_Should_Return_Correct_Size_For_Uniform_Stretch() { - var bitmap = Mock.Of(x => x.PixelWidth == 50 && x.PixelHeight == 100); + var bitmap = Mock.Of(x => x.Size == new Size(50, 100)); var target = new Image(); target.Stretch = Stretch.Uniform; target.Source = bitmap; @@ -52,7 +52,7 @@ public void Measure_Should_Return_Correct_Size_For_Uniform_Stretch() [Fact] public void Measure_Should_Return_Correct_Size_For_UniformToFill_Stretch() { - var bitmap = Mock.Of(x => x.PixelWidth == 50 && x.PixelHeight == 100); + var bitmap = Mock.Of(x => x.Size == new Size(50, 100)); var target = new Image(); target.Stretch = Stretch.UniformToFill; target.Source = bitmap; diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs index 4cee05a0d91..e6cd800529b 100644 --- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs +++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs @@ -29,14 +29,13 @@ public BitmapTests() class Framebuffer : ILockedFramebuffer, IFramebufferPlatformSurface { - public Framebuffer(PixelFormat fmt, int width, int height) + public Framebuffer(PixelFormat fmt, PixelSize size) { Format = fmt; var bpp = fmt == PixelFormat.Rgb565 ? 2 : 4; - Width = width; - Height = height; - RowBytes = bpp * width; - Address = Marshal.AllocHGlobal(Height * RowBytes); + Size = size; + RowBytes = bpp * size.Width; + Address = Marshal.AllocHGlobal(size.Height * RowBytes); } public IntPtr Address { get; } @@ -45,12 +44,10 @@ public Framebuffer(PixelFormat fmt, int width, int height) public PixelFormat Format { get; } - public int Height { get; } + public PixelSize Size { get; } public int RowBytes { get; } - public int Width { get; } - public void Dispose() { //no-op @@ -74,7 +71,7 @@ public ILockedFramebuffer Lock() public void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormat fmt) { var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt; - var fb = new Framebuffer(fmt, 80, 80); + var fb = new Framebuffer(fmt, new PixelSize(80, 80)); var r = Avalonia.AvaloniaLocator.Current.GetService(); using (var target = r.CreateRenderTarget(new object[] { fb })) using (var ctx = target.CreateDrawingContext(null)) @@ -87,9 +84,9 @@ public void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormat fmt) ctx.PopOpacity(); } - var bmp = new Bitmap(fmt, fb.Address, fb.Width, fb.Height, fb.RowBytes); + var bmp = new Bitmap(fmt, fb.Address, fb.Size, new Vector(96, 96), fb.RowBytes); fb.Deallocate(); - using (var rtb = new RenderTargetBitmap(100, 100)) + using (var rtb = new RenderTargetBitmap(new PixelSize(100, 100), new Vector(96, 96))) { using (var ctx = rtb.CreateDrawingContext(null)) { @@ -108,7 +105,7 @@ public void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormat fmt) [InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgba8888)] public void WriteableBitmapShouldBeUsable(PixelFormat fmt) { - var writeableBitmap = new WriteableBitmap(256, 256, fmt); + var writeableBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), fmt); var data = new int[256 * 256]; for (int y = 0; y < 256; y++) diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 19413b32eb0..2892615e485 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -73,22 +73,21 @@ protected async Task RenderToFile(Control target, [CallerMemberName] string test var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png"); var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png"); var factory = AvaloniaLocator.Current.GetService(); + var pixelSize = new PixelSize((int)target.Width, (int)target.Height); + var size = new Size(target.Width, target.Height); + var dpi = new Vector(96, 96); - using (RenderTargetBitmap bitmap = new RenderTargetBitmap( - (int)target.Width, - (int)target.Height)) + using (RenderTargetBitmap bitmap = new RenderTargetBitmap(pixelSize, dpi)) { - Size size = new Size(target.Width, target.Height); target.Measure(size); target.Arrange(new Rect(size)); bitmap.Render(target); bitmap.Save(immediatePath); } - using (var rtb = factory.CreateRenderTargetBitmap((int)target.Width, (int)target.Height, 96, 96)) + using (var rtb = factory.CreateRenderTargetBitmap(pixelSize, dpi)) using (var renderer = new DeferredRenderer(target, rtb)) { - Size size = new Size(target.Width, target.Height); target.Measure(size); target.Arrange(new Rect(size)); renderer.UnitTestUpdateScene(); diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index de2b5179569..f8ad7e63f2a 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -25,11 +25,7 @@ public IRenderTarget CreateRenderTarget(IEnumerable surfaces) return Mock.Of(); } - public IRenderTargetBitmapImpl CreateRenderTargetBitmap( - int width, - int height, - double dpiX, - double dpiY) + public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi) { return Mock.Of(); } @@ -39,7 +35,10 @@ public IStreamGeometryImpl CreateStreamGeometry() return new MockStreamGeometryImpl(); } - public IWriteableBitmapImpl CreateWriteableBitmap(int width, int height, PixelFormat? format = default(PixelFormat?)) + public IWriteableBitmapImpl CreateWriteableBitmap( + PixelSize size, + Vector dpi, + PixelFormat? format = default(PixelFormat?)) { throw new NotImplementedException(); } @@ -54,7 +53,12 @@ public IBitmapImpl LoadBitmap(string fileName) return Mock.Of(); } - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, int width, int height, int stride) + public IBitmapImpl LoadBitmap( + PixelFormat format, + IntPtr data, + PixelSize size, + Vector dpi, + int stride) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs index 93b5a8a7644..92c12c6b9ea 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs @@ -24,7 +24,7 @@ public IRenderTarget CreateRenderTarget(IEnumerable surfaces) throw new NotImplementedException(); } - public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height, double dpiX, double dpiY) + public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi) { throw new NotImplementedException(); } @@ -44,12 +44,12 @@ public IBitmapImpl LoadBitmap(string fileName) throw new NotImplementedException(); } - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, int width, int height, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) { throw new NotImplementedException(); } - public IWriteableBitmapImpl CreateWriteableBitmap(int width, int height, PixelFormat? fmt) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? fmt) { throw new NotImplementedException(); } From 465b2cd0dab99911ed0a6dae04483dad51c41761 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 11 Sep 2018 23:49:40 +0200 Subject: [PATCH 2/4] Add PixelSize to mobile APIs. --- .../Platform/SkiaPlatform/AndroidFramebuffer.cs | 8 ++++---- src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs | 12 +++++------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs index 405da129676..2afa4e83f1f 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs @@ -20,9 +20,10 @@ public AndroidFramebuffer(Surface surface) ANativeWindow_Buffer buffer; var rc = new ARect() { - right = Width = ANativeWindow_getWidth(_window), - bottom = Height = ANativeWindow_getHeight(_window) + right = ANativeWindow_getWidth(_window), + bottom = ANativeWindow_getHeight(_window) }; + Size = new PixelSize(rc.right, rc.bottom); ANativeWindow_lock(_window, out buffer, ref rc); Format = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565 @@ -41,8 +42,7 @@ public void Dispose() } public IntPtr Address { get; set; } - public int Width { get; } - public int Height { get; } + public PixelSize Size { get; } public int RowBytes { get; } public Vector Dpi { get; } = new Vector(96, 96); public PixelFormat Format { get; } diff --git a/src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs b/src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs index b3b41fe724e..89c7aaf76ce 100644 --- a/src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs +++ b/src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs @@ -23,12 +23,11 @@ public EmulatedFramebuffer(UIView view) var frame = view.Frame; _viewWidth = frame.Width; _viewHeight = frame.Height; - Width = (int) frame.Width * factor; - Height = (int) frame.Height * factor; - RowBytes = Width * 4; + Size = new PixelSize((int)frame.Width * factor, (int)frame.Height * factor); + RowBytes = Size.Width * 4; Dpi = new Vector(96, 96) * factor; Format = PixelFormat.Rgba8888; - Address = Marshal.AllocHGlobal(Height * RowBytes); + Address = Marshal.AllocHGlobal(Size.Height * RowBytes); } public void Dispose() @@ -37,7 +36,7 @@ public void Dispose() return; var nfo = (int) CGBitmapFlags.ByteOrder32Big | (int) CGImageAlphaInfo.PremultipliedLast; using (var colorSpace = CGColorSpace.CreateDeviceRGB()) - using (var bContext = new CGBitmapContext(Address, Width, Height, 8, Width * 4, + using (var bContext = new CGBitmapContext(Address, Size.Width, Size.Height, 8, Size.Width * 4, colorSpace, (CGImageAlphaInfo) nfo)) using (var image = bContext.ToImage()) using (var context = UIGraphics.GetCurrentContext()) @@ -52,8 +51,7 @@ public void Dispose() } public IntPtr Address { get; private set; } - public int Width { get; } - public int Height { get; } + public PixelSize Size { get; } public int RowBytes { get; } public Vector Dpi { get; } public PixelFormat Format { get; } From 0de606b995353ebf215db6ef785648db4096b8ff Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 11 Sep 2018 23:57:50 +0200 Subject: [PATCH 3/4] Fix failing tests. --- tests/Avalonia.Controls.UnitTests/ImageTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ImageTests.cs b/tests/Avalonia.Controls.UnitTests/ImageTests.cs index 7a0e5681d7e..e92fc572b4b 100644 --- a/tests/Avalonia.Controls.UnitTests/ImageTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ImageTests.cs @@ -13,7 +13,7 @@ public class ImageTests [Fact] public void Measure_Should_Return_Correct_Size_For_No_Stretch() { - var bitmap = Mock.Of(x => x.Size == new Size(50, 100)); + var bitmap = Mock.Of(x => x.PixelSize == new PixelSize(50, 100)); var target = new Image(); target.Stretch = Stretch.None; target.Source = bitmap; @@ -26,7 +26,7 @@ public void Measure_Should_Return_Correct_Size_For_No_Stretch() [Fact] public void Measure_Should_Return_Correct_Size_For_Fill_Stretch() { - var bitmap = Mock.Of(x => x.Size == new Size(50, 100)); + var bitmap = Mock.Of(x => x.PixelSize == new PixelSize(50, 100)); var target = new Image(); target.Stretch = Stretch.Fill; target.Source = bitmap; @@ -39,7 +39,7 @@ public void Measure_Should_Return_Correct_Size_For_Fill_Stretch() [Fact] public void Measure_Should_Return_Correct_Size_For_Uniform_Stretch() { - var bitmap = Mock.Of(x => x.Size == new Size(50, 100)); + var bitmap = Mock.Of(x => x.PixelSize == new PixelSize(50, 100)); var target = new Image(); target.Stretch = Stretch.Uniform; target.Source = bitmap; @@ -52,7 +52,7 @@ public void Measure_Should_Return_Correct_Size_For_Uniform_Stretch() [Fact] public void Measure_Should_Return_Correct_Size_For_UniformToFill_Stretch() { - var bitmap = Mock.Of(x => x.Size == new Size(50, 100)); + var bitmap = Mock.Of(x => x.PixelSize == new PixelSize(50, 100)); var target = new Image(); target.Stretch = Stretch.UniformToFill; target.Source = bitmap; From f2d126b9d2ee4cb47f7a80a4f580f8f09910c37a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 29 Oct 2018 10:38:27 +0000 Subject: [PATCH 4/4] update avalonia.native to use PixelSize. --- src/Avalonia.Native/DeferredFramebuffer.cs | 9 ++++----- src/Avalonia.Native/WindowImplBase.cs | 6 ++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs index 4bee59266c5..adc721de426 100644 --- a/src/Avalonia.Native/DeferredFramebuffer.cs +++ b/src/Avalonia.Native/DeferredFramebuffer.cs @@ -18,15 +18,14 @@ public DeferredFramebuffer(Func, bool> lockWindow, { _lockWindow = lockWindow; Address = Marshal.AllocHGlobal(width * height * 4); - Width = width; - Height = height; + Size = new PixelSize(width, height); RowBytes = width * 4; Dpi = dpi; Format = PixelFormat.Rgba8888; } public IntPtr Address { get; set; } - public int Width { get; set; } + public PixelSize Size { get; set; } public int Height { get; set; } public int RowBytes { get; set; } public Vector Dpi { get; set; } @@ -66,8 +65,8 @@ public void Dispose() X = Dpi.X, Y = Dpi.Y }, - Width = Width, - Height = Height, + Width = Size.Width, + Height = Size.Height, PixelFormat = (AvnPixelFormat)Format, Stride = RowBytes }; diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index e81e912a0a6..aaaba44fff4 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -100,15 +100,13 @@ class FramebufferWrapper : ILockedFramebuffer public FramebufferWrapper(AvnFramebuffer fb) { Address = fb.Data; - Width = fb.Width; - Height = fb.Height; + Size = new PixelSize(fb.Width, fb.Height); RowBytes = fb.Stride; Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y); Format = (PixelFormat)fb.PixelFormat; } public IntPtr Address { get; set; } - public int Width { get; set; } - public int Height { get; set; } + public PixelSize Size { get; set; } public int RowBytes {get;set;} public Vector Dpi { get; set; } public PixelFormat Format { get; }