Browse Source

Merge branch 'master' into allow-animation-of-structs-without-typeConverter

pull/2040/head
Jumar Macato 7 years ago
committed by GitHub
parent
commit
8816387d4e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
  2. 5
      src/Avalonia.Controls/Image.cs
  3. 8
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  4. 2
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  5. 9
      src/Avalonia.Native/DeferredFramebuffer.cs
  6. 6
      src/Avalonia.Native/WindowImplBase.cs
  7. 31
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  8. 21
      src/Avalonia.Visuals/Media/Imaging/IBitmap.cs
  9. 29
      src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs
  10. 11
      src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs
  11. 174
      src/Avalonia.Visuals/Media/PixelSize.cs
  12. 8
      src/Avalonia.Visuals/Platform/IBitmapImpl.cs
  13. 9
      src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs
  14. 32
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  15. 10
      src/Avalonia.Visuals/Platform/LockedFramebuffer.cs
  16. 8
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  17. 10
      src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
  18. 18
      src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs
  19. 9
      src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs
  20. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
  21. 6
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  22. 2
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  23. 27
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  24. 28
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  25. 15
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  26. 26
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  27. 20
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  28. 4
      src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
  29. 8
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  30. 4
      src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
  31. 4
      src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
  32. 4
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs
  33. 3
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
  34. 38
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  35. 12
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
  36. 7
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
  37. 4
      src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs
  38. 3
      src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs
  39. 4
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  40. 28
      src/Windows/Avalonia.Win32/WindowFramebuffer.cs
  41. 12
      src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs
  42. 8
      tests/Avalonia.Controls.UnitTests/ImageTests.cs
  43. 21
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  44. 11
      tests/Avalonia.RenderTests/TestBase.cs
  45. 18
      tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
  46. 6
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

8
src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs

@ -20,9 +20,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
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 @@ namespace Avalonia.Android.Platform.SkiaPlatform
}
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; }

5
src/Avalonia.Controls/Image.cs

@ -58,7 +58,7 @@ namespace Avalonia.Controls
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
@ -84,8 +84,7 @@ namespace Avalonia.Controls
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;

8
src/Avalonia.Controls/Remote/RemoteWidget.cs

@ -73,9 +73,9 @@ namespace Avalonia.Controls.Remote
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;
@ -83,7 +83,7 @@ namespace Avalonia.Controls.Remote
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);

2
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -267,7 +267,7 @@ namespace Avalonia.Controls.Remote.Server
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));
}

9
src/Avalonia.Native/DeferredFramebuffer.cs

@ -18,15 +18,14 @@ namespace Avalonia.Native
{
_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 @@ namespace Avalonia.Native
X = Dpi.X,
Y = Dpi.Y
},
Width = Width,
Height = Height,
Width = Size.Width,
Height = Size.Height,
PixelFormat = (AvnPixelFormat)Format,
Stride = RowBytes
};

6
src/Avalonia.Native/WindowImplBase.cs

@ -100,15 +100,13 @@ namespace Avalonia.Native
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; }

31
src/Avalonia.Visuals/Media/Imaging/Bitmap.cs

@ -56,30 +56,29 @@ namespace Avalonia.Media.Imaging
{
PlatformImpl.Dispose();
}
/// <summary>
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>
/// <param name="format">Pixel format</param>
/// <param name="data">Pointer to source bytes</param>
/// <param name="width">Bitmap width</param>
/// <param name="height">Bitmap height</param>
/// <param name="stride">Bytes per row</param>
public Bitmap(PixelFormat format, IntPtr data, int width, int height, int stride)
/// <param name="format">The pixel format.</param>
/// <param name="data">The pointer to the source bytes.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="stride">The number of bytes per row.</param>
public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
{
PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
.LoadBitmap(format, data, width, height, stride));
.LoadBitmap(format, data, size, dpi, stride));
}
/// <summary>
/// Gets the width of the bitmap, in pixels.
/// </summary>
public int PixelWidth => PlatformImpl.Item.PixelWidth;
/// <inheritdoc/>
public Vector Dpi => PlatformImpl.Item.Dpi;
/// <summary>
/// Gets the height of the bitmap, in pixels.
/// </summary>
public int PixelHeight => PlatformImpl.Item.PixelHeight;
/// <inheritdoc/>
public Size Size => PlatformImpl.Item.PixelSize.ToSize(Dpi);
/// <inheritdoc/>
public PixelSize PixelSize => PlatformImpl.Item.PixelSize;
/// <summary>
/// Gets the platform-specific bitmap implementation.

21
src/Avalonia.Visuals/Media/Imaging/IBitmap.cs

@ -14,20 +14,33 @@ namespace Avalonia.Media.Imaging
public interface IBitmap : IDisposable
{
/// <summary>
/// Gets the width of the bitmap, in pixels.
/// Gets the dots per inch (DPI) of the image.
/// </summary>
int PixelWidth { get; }
/// <remarks>
/// Note that Skia does not currently support reading the DPI of an image so this value
/// will always be 96dpi on Skia.
/// </remarks>
Vector Dpi { get; }
/// <summary>
/// Gets the height of the bitmap, in pixels.
/// Gets the size of the bitmap, in device pixels.
/// </summary>
int PixelHeight { get; }
PixelSize PixelSize { get; }
/// <summary>
/// Gets the platform-specific bitmap implementation.
/// </summary>
IRef<IBitmapImpl> PlatformImpl { get; }
/// <summary>
/// Gets the size of the image, in device independent pixels.
/// </summary>
/// <remarks>
/// Note that Skia does not currently support reading the DPI of an image so this value
/// will equal <see cref="PixelSize"/> on Skia.
/// </remarks>
Size Size { get; }
/// <summary>
/// Saves the bitmap to a file.
/// </summary>

29
src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs

@ -17,12 +17,19 @@ namespace Avalonia.Media.Imaging
/// <summary>
/// Initializes a new instance of the <see cref="RenderTargetBitmap"/> class.
/// </summary>
/// <param name="pixelWidth">The width of the bitmap in pixels.</param>
/// <param name="pixelHeight">The height of the bitmap in pixels.</param>
/// <param name="dpiX">The horizontal DPI of the bitmap.</param>
/// <param name="dpiY">The vertical DPI of the bitmap.</param>
public RenderTargetBitmap(int pixelWidth, int pixelHeight, double dpiX = 96, double dpiY = 96)
: this(RefCountable.Create(CreateImpl(pixelWidth, pixelHeight, dpiX, dpiY)))
/// <param name="pixelSize">The size of the bitmap.</param>
public RenderTargetBitmap(PixelSize pixelSize)
: this(pixelSize, new Vector(96, 96))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RenderTargetBitmap"/> class.
/// </summary>
/// <param name="pixelSize">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
public RenderTargetBitmap(PixelSize pixelSize, Vector dpi)
: this(RefCountable.Create(CreateImpl(pixelSize, dpi)))
{
}
@ -45,15 +52,13 @@ namespace Avalonia.Media.Imaging
/// <summary>
/// Creates a platform-specific implementation for a <see cref="RenderTargetBitmap"/>.
/// </summary>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="dpiX">The horizontal DPI of the bitmap.</param>
/// <param name="dpiY">The vertical DPI of the bitmap.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <returns>The platform-specific implementation.</returns>
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<IPlatformRenderInterface>();
return factory.CreateRenderTargetBitmap(width, height, dpiX, dpiY);
return factory.CreateRenderTargetBitmap(size, dpi);
}
/// <inheritdoc/>

11
src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs

@ -7,8 +7,15 @@ namespace Avalonia.Media.Imaging
/// </summary>
public class WriteableBitmap : Bitmap
{
public WriteableBitmap(int width, int height, PixelFormat? format = null)
: base(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().CreateWriteableBitmap(width, height, format))
/// <summary>
/// Initializes a new instance of the <see cref="WriteableBitmap"/> class.
/// </summary>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">The pixel format (optional).</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
: base(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().CreateWriteableBitmap(size, dpi, format))
{
}

174
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
{
/// <summary>
/// Represents a size in device pixels.
/// </summary>
public readonly struct PixelSize
{
/// <summary>
/// A size representing zero
/// </summary>
public static readonly PixelSize Empty = new PixelSize(0, 0);
/// <summary>
/// Initializes a new instance of the <see cref="PixelSize"/> structure.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public PixelSize(int width, int height)
{
Width = width;
Height = height;
}
/// <summary>
/// Gets the aspect ratio of the size.
/// </summary>
public double AspectRatio => (double)Width / Height;
/// <summary>
/// Gets the width.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
public int Height { get; }
/// <summary>
/// Checks for equality between two <see cref="PixelSize"/>s.
/// </summary>
/// <param name="left">The first size.</param>
/// <param name="right">The second size.</param>
/// <returns>True if the sizes are equal; otherwise false.</returns>
public static bool operator ==(PixelSize left, PixelSize right)
{
return left.Width == right.Width && left.Height == right.Height;
}
/// <summary>
/// Checks for inequality between two <see cref="Size"/>s.
/// </summary>
/// <param name="left">The first size.</param>
/// <param name="right">The second size.</param>
/// <returns>True if the sizes are unequal; otherwise false.</returns>
public static bool operator !=(PixelSize left, PixelSize right)
{
return !(left == right);
}
/// <summary>
/// Parses a <see cref="PixelSize"/> string.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="PixelSize"/>.</returns>
public static PixelSize Parse(string s)
{
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Size"))
{
return new PixelSize(
tokenizer.ReadInt32(),
tokenizer.ReadInt32());
}
}
/// <summary>
/// Checks for equality between a size and an object.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns>
/// True if <paramref name="obj"/> is a size that equals the current size.
/// </returns>
public override bool Equals(object obj)
{
if (obj is PixelSize other)
{
return this == other;
}
return false;
}
/// <summary>
/// Returns a hash code for a <see cref="PixelSize"/>.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = (hash * 23) + Width.GetHashCode();
hash = (hash * 23) + Height.GetHashCode();
return hash;
}
}
/// <summary>
/// Returns a new <see cref="PixelSize"/> with the same height and the specified width.
/// </summary>
/// <param name="width">The width.</param>
/// <returns>The new <see cref="PixelSize"/>.</returns>
public PixelSize WithWidth(int width) => new PixelSize(width, Height);
/// <summary>
/// Returns a new <see cref="PixelSize"/> with the same width and the specified height.
/// </summary>
/// <param name="height">The height.</param>
/// <returns>The new <see cref="PixelSize"/>.</returns>
public PixelSize WithHeight(int height) => new PixelSize(Width, height);
/// <summary>
/// Converts the <see cref="PixelSize"/> to a device-independent <see cref="Size"/> using the
/// specified dots per inch (DPI).
/// </summary>
/// <param name="dpi">The dots per inch.</param>
/// <returns>The device-independent size.</returns>
public Size ToSize(double dpi) => new Size(Width / (dpi / 96), Height / (dpi / 96));
/// <summary>
/// Converts the <see cref="PixelSize"/> to a device-independent <see cref="Size"/> using the
/// specified dots per inch (DPI).
/// </summary>
/// <param name="dpi">The dots per inch.</param>
/// <returns>The device-independent size.</returns>
public Size ToSize(Vector dpi) => new Size(Width / (dpi.X / 96), Height / (dpi.Y / 96));
/// <summary>
/// Converts a <see cref="Size"/> to device pixels using the specified dots per inch (DPI).
/// </summary>
/// <param name="size">The size.</param>
/// <param name="dpi">The dots per inch.</param>
/// <returns>The device-independent size.</returns>
public static PixelSize FromSize(Size size, double dpi) => new PixelSize(
(int)(size.Width * (dpi / 96)),
(int)(size.Height * (dpi / 96)));
/// <summary>
/// Converts a <see cref="Size"/> to device pixels using the specified dots per inch (DPI).
/// </summary>
/// <param name="size">The size.</param>
/// <param name="dpi">The dots per inch.</param>
/// <returns>The device-independent size.</returns>
public static PixelSize FromSize(Size size, Vector dpi) => new PixelSize(
(int)(size.Width * (dpi.X / 96)),
(int)(size.Height * (dpi.Y / 96)));
/// <summary>
/// Returns the string representation of the size.
/// </summary>
/// <returns>The string representation of the size.</returns>
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", Width, Height);
}
}
}

8
src/Avalonia.Visuals/Platform/IBitmapImpl.cs

@ -12,14 +12,14 @@ namespace Avalonia.Platform
public interface IBitmapImpl : IDisposable
{
/// <summary>
/// Gets the width of the bitmap, in pixels.
/// Gets the dots per inch (DPI) of the image.
/// </summary>
int PixelWidth { get; }
Vector Dpi { get; }
/// <summary>
/// Gets the height of the bitmap, in pixels.
/// Gets the size of the bitmap, in device pixels.
/// </summary>
int PixelHeight { get; }
PixelSize PixelSize { get; }
/// <summary>
/// Version of the pixel data

9
src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs

@ -10,14 +10,9 @@ namespace Avalonia.Platform
IntPtr Address { get; }
/// <summary>
/// Framebuffer width
/// Gets the framebuffer size in device pixels.
/// </summary>
int Width { get; }
/// <summary>
/// Framebuffer height
/// </summary>
int Height { get; }
PixelSize Size{ get; }
/// <summary>
/// Number of bytes per row

32
src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

@ -49,25 +49,19 @@ namespace Avalonia.Platform
/// <summary>
/// Creates a render target bitmap implementation.
/// </summary>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="dpiX">The horizontal DPI of the bitmap.</param>
/// <param name="dpiY">The vertical DPI of the bitmap.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <returns>An <see cref="IRenderTargetBitmapImpl"/>.</returns>
IRenderTargetBitmapImpl CreateRenderTargetBitmap(
int width,
int height,
double dpiX,
double dpiY);
IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi);
/// <summary>
/// Creates a writeable bitmap implementation.
/// </summary>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">Pixel format (optional).</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
IWriteableBitmapImpl CreateWriteableBitmap(int width, int height, PixelFormat? format = null);
IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null);
/// <summary>
/// Loads a bitmap implementation from a file..
@ -84,14 +78,14 @@ namespace Avalonia.Platform
IBitmapImpl LoadBitmap(Stream stream);
/// <summary>
/// Loads a bitmap implementation from a pixels in memory..
/// Loads a bitmap implementation from a pixels in memory.
/// </summary>
/// <param name="format">Pixel format</param>
/// <param name="data">Pointer to source bytes</param>
/// <param name="width">Bitmap width</param>
/// <param name="height">Bitmap height</param>
/// <param name="stride">Bytes per row</param>
/// <param name="format">The pixel format.</param>
/// <param name="data">The pointer to source bytes.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="stride">The number of bytes per row.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, int width, int height, int stride);
IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride);
}
}

10
src/Avalonia.Visuals/Platform/LockedFramebuffer.cs

@ -6,21 +6,19 @@ namespace Avalonia.Platform
{
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 @@ namespace Avalonia.Platform
_onDispose?.Invoke();
}
}
}
}

8
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -356,7 +356,7 @@ namespace Avalonia.Rendering
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)
{
@ -380,7 +380,7 @@ namespace Avalonia.Rendering
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);
}
@ -432,8 +432,8 @@ namespace Avalonia.Rendering
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));

10
src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs

@ -22,8 +22,7 @@ namespace Avalonia.Gtk3
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 @@ namespace Avalonia.Gtk3
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; }

18
src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs

@ -13,12 +13,11 @@ namespace Avalonia.Gtk3
{
_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<IRuntimePlatform>().AllocBlob(RowBytes * Height);
_blob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(RowBytes * Size.Height);
Address = _blob.Address;
}
@ -26,8 +25,8 @@ namespace Avalonia.Gtk3
{
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 @@ namespace Avalonia.Gtk3
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 @@ namespace Avalonia.Gtk3
}
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; }

9
src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs

@ -19,7 +19,7 @@ namespace Avalonia.LinuxFramebuffer
_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 @@ namespace Avalonia.LinuxFramebuffer
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;
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github

@ -1 +1 @@
Subproject commit 8e4700d4b24935ed5400e5e0d6fce96b5b4a317a
Subproject commit 8abbe09592668efb573ac4d5548ba2d7e464ba78

6
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -142,7 +142,7 @@ namespace Avalonia.Skia
public void DrawImage(IRef<IBitmapImpl> 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();
}
@ -391,7 +391,7 @@ namespace Avalonia.Skia
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,
@ -401,7 +401,7 @@ namespace Avalonia.Skia
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);

2
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -41,7 +41,7 @@ namespace Avalonia.Skia
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);

27
src/Skia/Avalonia.Skia/ImmutableBitmap.cs

@ -31,22 +31,24 @@ namespace Avalonia.Skia
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);
}
}
/// <summary>
/// Create immutable bitmap from given pixel data copy.
/// </summary>
/// <param name="width">Width of data pixels.</param>
/// <param name="height">Height of data pixels.</param>
/// <param name="size">Size of the bitmap.</param>
/// <param name="dpi">DPI of the bitmap.</param>
/// <param name="stride">Stride of data pixels.</param>
/// <param name="format">Format of data pixels.</param>
/// <param name="data">Data pixels.</param>
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 @@ namespace Avalonia.Skia
throw new ArgumentException("Unable to create bitmap from provided data");
}
PixelWidth = width;
PixelHeight = height;
PixelSize = size;
Dpi = dpi;
}
/// <inheritdoc />
public int PixelWidth { get; }
/// <inheritdoc />
public int PixelHeight { get; }
public Vector Dpi { get; }
public PixelSize PixelSize { get; }
public int Version { get; } = 1;
@ -91,4 +90,4 @@ namespace Avalonia.Skia
context.Canvas.DrawImage(_image, sourceRect, destRect, paint);
}
}
}
}

28
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -67,34 +67,28 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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,
GrContext = GrContext
@ -123,9 +117,9 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
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);
}
}
}

15
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -16,7 +16,6 @@ namespace Avalonia.Skia
/// </summary>
public class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
{
private readonly Vector _dpi;
private readonly SKSurface _surface;
private readonly SKCanvas _canvas;
private readonly bool _disableLcdRendering;
@ -28,12 +27,12 @@ namespace Avalonia.Skia
/// <param name="createInfo">Create info.</param>
public SurfaceRenderTarget(CreateInfo createInfo)
{
PixelWidth = createInfo.Width;
PixelHeight = createInfo.Height;
_dpi = createInfo.Dpi;
PixelSize = new PixelSize(createInfo.Width, createInfo.Height);
Dpi = createInfo.Dpi;
_disableLcdRendering = createInfo.DisableTextLcdRendering;
_grContext = createInfo.GrContext;
_surface = CreateSurface(createInfo.GrContext, PixelWidth, PixelHeight, createInfo.Format);
_surface = CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, createInfo.Format);
_canvas = _surface?.Canvas;
@ -74,7 +73,7 @@ namespace Avalonia.Skia
var createInfo = new DrawingContextImpl.CreateInfo
{
Canvas = _canvas,
Dpi = _dpi,
Dpi = Dpi,
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = _disableLcdRendering,
GrContext = _grContext
@ -84,10 +83,10 @@ namespace Avalonia.Skia
}
/// <inheritdoc />
public int PixelWidth { get; }
public Vector Dpi { get; }
/// <inheritdoc />
public int PixelHeight { get; }
public PixelSize PixelSize { get; }
public int Version { get; private set; } = 1;

26
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -22,13 +22,13 @@ namespace Avalonia.Skia
/// <summary>
/// Create new writeable bitmap.
/// </summary>
/// <param name="width">Width.</param>
/// <param name="height">Height.</param>
/// <param name="format">Format.</param>
public WriteableBitmapImpl(int width, int height, PixelFormat? format = null)
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">The pixel format.</param>
public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat? format = null)
{
PixelHeight = height;
PixelWidth = width;
PixelSize = size;
Dpi = dpi;
var colorType = PixelFormatHelper.ResolveColorType(format);
@ -38,24 +38,23 @@ namespace Avalonia.Skia
{
_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);
}
/// <inheritdoc />
public int PixelWidth { get; }
public Vector Dpi { get; }
/// <inheritdoc />
public int PixelHeight { get; }
public PixelSize PixelSize { get; }
public int Version { get; private set; } = 1;
@ -146,10 +145,7 @@ namespace Avalonia.Skia
public IntPtr Address => _bitmap.GetPixels();
/// <inheritdoc />
public int Width => _bitmap.Width;
/// <inheritdoc />
public int Height => _bitmap.Height;
public PixelSize Size => new PixelSize(_bitmap.Width, _bitmap.Height);
/// <inheritdoc />
public int RowBytes => _bitmap.RowBytes;

20
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -110,9 +110,9 @@ namespace Avalonia.Direct2D1
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 @@ namespace Avalonia.Direct2D1
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 @@ namespace Avalonia.Direct2D1
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);
}
}
}

4
src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs

@ -40,7 +40,7 @@ namespace Avalonia.Direct2D1
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 @@ namespace Avalonia.Direct2D1
{
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),

8
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -330,12 +330,8 @@ namespace Avalonia.Direct2D1.Media
{
var platform = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
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);
}
}

4
src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs

@ -20,7 +20,7 @@ namespace Avalonia.Direct2D1.Media
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 @@ namespace Avalonia.Direct2D1.Media
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);

4
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 int Version { get; protected set; } = 1;
public abstract OptionalDispose<D2DBitmap> GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target);

4
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs

@ -30,8 +30,8 @@ namespace Avalonia.Direct2D1.Media
_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()
{

3
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs

@ -19,9 +19,6 @@ namespace Avalonia.Direct2D1.Media.Imaging
_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)

38
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@ -44,10 +44,10 @@ namespace Avalonia.Direct2D1.Media
/// <summary>
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
/// </summary>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="pixelFormat">Pixel format</param>
public WicBitmapImpl(int width, int height, APixelFormat? pixelFormat = null)
public WicBitmapImpl(PixelSize size, Vector dpi, APixelFormat? pixelFormat = null)
{
if (!pixelFormat.HasValue)
{
@ -57,19 +57,22 @@ namespace Avalonia.Direct2D1.Media
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),
@ -79,17 +82,18 @@ namespace Avalonia.Direct2D1.Media
}
}
protected APixelFormat? PixelFormat { get; }
public override Vector Dpi
{
get
{
WicImpl.GetResolution(out double x, out double y);
return new Vector(x, y);
}
}
/// <summary>
/// Gets the width of the bitmap, in pixels.
/// </summary>
public override int PixelWidth => WicImpl.Size.Width;
public override PixelSize PixelSize => WicImpl.Size.ToAvalonia();
/// <summary>
/// Gets the height of the bitmap, in pixels.
/// </summary>
public override int PixelHeight => WicImpl.Size.Height;
protected APixelFormat? PixelFormat { get; }
public override void Dispose()
{

12
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs

@ -13,17 +13,15 @@ namespace Avalonia.Direct2D1.Media
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(

7
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)
{
}
@ -36,8 +36,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
}
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;

4
src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs

@ -41,6 +41,10 @@ namespace Avalonia.Direct2D1
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);

3
src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs

@ -58,8 +58,7 @@ namespace Avalonia.Win32.Interop.Wpf
}
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;

4
src/Windows/Avalonia.Win32/FramebufferManager.cs

@ -21,11 +21,11 @@ namespace Avalonia.Win32
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;
}

28
src/Windows/Avalonia.Win32/WindowFramebuffer.cs

@ -11,21 +11,21 @@ namespace Avalonia.Win32
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<IRuntimePlatform>().AllocBlob(width * height * 4);
_bmpInfo.biWidth = size.Width;
_bmpInfo.biHeight = -size.Height;
_bitmapBlob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(size.Width * size.Height * 4);
}
~WindowFramebuffer()
@ -34,7 +34,7 @@ namespace Avalonia.Win32
}
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 @@ namespace Avalonia.Win32
}
}
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,

12
src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs

@ -23,12 +23,11 @@ namespace Avalonia.iOS
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 @@ namespace Avalonia.iOS
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 @@ namespace Avalonia.iOS
}
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; }

8
tests/Avalonia.Controls.UnitTests/ImageTests.cs

@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Measure_Should_Return_Correct_Size_For_No_Stretch()
{
var bitmap = Mock.Of<IBitmap>(x => x.PixelWidth == 50 && x.PixelHeight == 100);
var bitmap = Mock.Of<IBitmap>(x => x.PixelSize == new PixelSize(50, 100));
var target = new Image();
target.Stretch = Stretch.None;
target.Source = bitmap;
@ -26,7 +26,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Measure_Should_Return_Correct_Size_For_Fill_Stretch()
{
var bitmap = Mock.Of<IBitmap>(x => x.PixelWidth == 50 && x.PixelHeight == 100);
var bitmap = Mock.Of<IBitmap>(x => x.PixelSize == new PixelSize(50, 100));
var target = new Image();
target.Stretch = Stretch.Fill;
target.Source = bitmap;
@ -39,7 +39,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Measure_Should_Return_Correct_Size_For_Uniform_Stretch()
{
var bitmap = Mock.Of<IBitmap>(x => x.PixelWidth == 50 && x.PixelHeight == 100);
var bitmap = Mock.Of<IBitmap>(x => x.PixelSize == new PixelSize(50, 100));
var target = new Image();
target.Stretch = Stretch.Uniform;
target.Source = bitmap;
@ -52,7 +52,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Measure_Should_Return_Correct_Size_For_UniformToFill_Stretch()
{
var bitmap = Mock.Of<IBitmap>(x => x.PixelWidth == 50 && x.PixelHeight == 100);
var bitmap = Mock.Of<IBitmap>(x => x.PixelSize == new PixelSize(50, 100));
var target = new Image();
target.Stretch = Stretch.UniformToFill;
target.Source = bitmap;

21
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -29,14 +29,13 @@ namespace Avalonia.Direct2D1.RenderTests.Media
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 @@ namespace Avalonia.Direct2D1.RenderTests.Media
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 @@ namespace Avalonia.Direct2D1.RenderTests.Media
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<IPlatformRenderInterface>();
using (var target = r.CreateRenderTarget(new object[] { fb }))
using (var ctx = target.CreateDrawingContext(null))
@ -87,9 +84,9 @@ namespace Avalonia.Direct2D1.RenderTests.Media
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 @@ namespace Avalonia.Direct2D1.RenderTests.Media
[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++)

11
tests/Avalonia.RenderTests/TestBase.cs

@ -73,22 +73,21 @@ namespace Avalonia.Direct2D1.RenderTests
var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png");
var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png");
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
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();

18
tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs

@ -25,11 +25,7 @@ namespace Avalonia.UnitTests
return Mock.Of<IRenderTarget>();
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(
int width,
int height,
double dpiX,
double dpiY)
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
{
return Mock.Of<IRenderTargetBitmapImpl>();
}
@ -39,7 +35,10 @@ namespace Avalonia.UnitTests
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 @@ namespace Avalonia.UnitTests
return Mock.Of<IBitmapImpl>();
}
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();
}

6
tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

@ -24,7 +24,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
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 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
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();
}

Loading…
Cancel
Save