From 972587130b806a0f84795bc6b78ca9c990469dc1 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 9 Jul 2020 23:35:30 +0200 Subject: [PATCH 01/18] Support passing alpha format to created bitmaps. --- samples/RenderDemo/MainWindow.xaml | 3 + .../RenderDemo/Pages/WriteableBitmapPage.cs | 92 +++++++++++++++++++ src/Avalonia.Visuals/Media/Imaging/Bitmap.cs | 5 +- .../Media/Imaging/WriteableBitmap.cs | 5 +- src/Avalonia.Visuals/Platform/AlphaFormat.cs | 9 ++ .../Platform/IPlatformRenderInterface.cs | 7 +- src/Skia/Avalonia.Skia/ImmutableBitmap.cs | 9 +- .../Avalonia.Skia/PlatformRenderInterface.cs | 10 +- src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs | 22 +++++ src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 9 +- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 13 +-- .../Media/Imaging/WicBitmapImpl.cs | 17 ++-- .../Media/Imaging/WriteableWicBitmapImpl.cs | 4 +- .../Avalonia.Direct2D1/PrimitiveExtensions.cs | 9 +- 14 files changed, 172 insertions(+), 42 deletions(-) create mode 100644 samples/RenderDemo/Pages/WriteableBitmapPage.cs create mode 100644 src/Avalonia.Visuals/Platform/AlphaFormat.cs diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index 14ccc82043..770960d7c4 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -44,6 +44,9 @@ + + + diff --git a/samples/RenderDemo/Pages/WriteableBitmapPage.cs b/samples/RenderDemo/Pages/WriteableBitmapPage.cs new file mode 100644 index 0000000000..850e398a93 --- /dev/null +++ b/samples/RenderDemo/Pages/WriteableBitmapPage.cs @@ -0,0 +1,92 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Avalonia.LogicalTree; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Media.Immutable; +using Avalonia.Platform; +using Avalonia.Threading; + +namespace RenderDemo.Pages +{ + public class WriteableBitmapPage : Control + { + private WriteableBitmap _unpremulBitmap; + private WriteableBitmap _premulBitmap; + private readonly Stopwatch _st = Stopwatch.StartNew(); + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + _unpremulBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Unpremul); + _premulBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul); + + base.OnAttachedToLogicalTree(e); + } + + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnDetachedFromLogicalTree(e); + + _unpremulBitmap?.Dispose(); + _unpremulBitmap = null; + + _premulBitmap?.Dispose(); + _unpremulBitmap = null; + } + + public override void Render(DrawingContext context) + { + void FillPixels(WriteableBitmap bitmap, byte fillAlpha, bool premul) + { + using (var fb = bitmap.Lock()) + { + var data = new int[fb.Size.Width * fb.Size.Height]; + + for (int y = 0; y < fb.Size.Height; y++) + { + for (int x = 0; x < fb.Size.Width; x++) + { + var color = new Color(fillAlpha, 0, 255, 0); + + if (premul) + { + byte r = (byte) (color.R * color.A / 255); + byte g = (byte) (color.G * color.A / 255); + byte b = (byte) (color.B * color.A / 255); + + color = new Color(fillAlpha, r, g, b); + } + + data[y * fb.Size.Width + x] = (int) color.ToUint32(); + } + } + + Marshal.Copy(data, 0, fb.Address, fb.Size.Width * fb.Size.Height); + } + } + + base.Render(context); + + byte alpha = (byte)((_st.ElapsedMilliseconds / 10) % 256); + + FillPixels(_unpremulBitmap, alpha, false); + FillPixels(_premulBitmap, alpha, true); + + context.FillRectangle(Brushes.Red, new Rect(0, 0, 256 * 3, 256)); + + context.DrawImage(_unpremulBitmap, + new Rect(0, 0, 256, 256), + new Rect(0, 0, 256, 256)); + + context.DrawImage(_premulBitmap, + new Rect(0, 0, 256, 256), + new Rect(256, 0, 256, 256)); + + context.FillRectangle(new ImmutableSolidColorBrush(Colors.Lime, alpha / 255d), new Rect(512, 0, 256, 256)); + + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + } + } +} diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs index 86e2700c04..f86ce326d5 100644 --- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs @@ -99,14 +99,15 @@ namespace Avalonia.Media.Imaging /// Initializes a new instance of the class. /// /// The pixel format. + /// The alpha 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) + public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService() - .LoadBitmap(format, data, size, dpi, stride)); + .LoadBitmap(format, alphaFormat, data, size, dpi, stride)); } /// diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs index 88c0799cb9..b93706e6e5 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs @@ -13,9 +13,10 @@ namespace Avalonia.Media.Imaging /// The size of the bitmap in device pixels. /// The DPI of the bitmap. /// The pixel format (optional). + /// The alpha format (optional). /// An . - public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) - : base(AvaloniaLocator.Current.GetService().CreateWriteableBitmap(size, dpi, format)) + public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) + : base(AvaloniaLocator.Current.GetService().CreateWriteableBitmap(size, dpi, format, alphaFormat)) { } diff --git a/src/Avalonia.Visuals/Platform/AlphaFormat.cs b/src/Avalonia.Visuals/Platform/AlphaFormat.cs new file mode 100644 index 0000000000..ce8dcde728 --- /dev/null +++ b/src/Avalonia.Visuals/Platform/AlphaFormat.cs @@ -0,0 +1,9 @@ +namespace Avalonia.Platform +{ + public enum AlphaFormat + { + Premul, + Unpremul, + Opaque + } +} diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index ba30272b7b..5906d4080e 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; using Avalonia.Media; -using Avalonia.Media.Imaging; using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Platform @@ -83,8 +82,9 @@ namespace Avalonia.Platform /// The size of the bitmap in device pixels. /// The DPI of the bitmap. /// Pixel format (optional). + /// Alpha format (optional). /// An . - IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null); + IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null); /// /// Loads a bitmap implementation from a file.. @@ -124,12 +124,13 @@ namespace Avalonia.Platform /// Loads a bitmap implementation from a pixels in memory. /// /// The pixel format. + /// The alpha 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, PixelSize size, Vector dpi, int stride); + IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride); /// /// Creates a platform implementation of a glyph run. diff --git a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs index e84c7e34de..3010f49420 100644 --- a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs +++ b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs @@ -85,10 +85,6 @@ namespace Avalonia.Skia if (bmp.Width != desired.Width || bmp.Height != desired.Height) { - if (bmp.Height != bmp.Width) - { - - } var scaledBmp = bmp.Resize(desired, interpolationMode.ToSKFilterQuality()); bmp.Dispose(); bmp = scaledBmp; @@ -116,10 +112,11 @@ namespace Avalonia.Skia /// DPI of the bitmap. /// Stride of data pixels. /// Format of data pixels. + /// Alpha format of data pixels. /// Data pixels. - public ImmutableBitmap(PixelSize size, Vector dpi, int stride, PixelFormat format, IntPtr data) + public ImmutableBitmap(PixelSize size, Vector dpi, int stride, PixelFormat format, AlphaFormat alphaFormat, IntPtr data) { - var imageInfo = new SKImageInfo(size.Width, size.Height, format.ToSkColorType(), SKAlphaType.Premul); + var imageInfo = new SKImageInfo(size.Width, size.Height, format.ToSkColorType(), alphaFormat.ToSkAlphaType()); _image = SKImage.FromPixelCopy(imageInfo, data, stride); diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 66b7fdea13..8c53ac2b4e 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -2,11 +2,9 @@ using System; using System.Collections; using System.Collections.Generic; using System.IO; -using System.Security.Cryptography; using System.Linq; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Media; -using Avalonia.Media.Imaging; using Avalonia.OpenGL; using Avalonia.OpenGL.Imaging; using Avalonia.Platform; @@ -76,9 +74,9 @@ namespace Avalonia.Skia } /// - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { - return new ImmutableBitmap(size, dpi, stride, format, data); + return new ImmutableBitmap(size, dpi, stride, format, alphaFormat, data); } /// @@ -152,9 +150,9 @@ namespace Avalonia.Skia } /// - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) { - return new WriteableBitmapImpl(size, dpi, format); + return new WriteableBitmapImpl(size, dpi, format, alphaFormat); } private static readonly SKFont s_font = new SKFont diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index 1e772ef067..bb3dbbfadc 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs @@ -105,6 +105,28 @@ namespace Avalonia.Skia throw new ArgumentException("Unknown pixel format: " + fmt); } + public static SKAlphaType ToSkAlphaType(this AlphaFormat fmt) + { + return fmt switch + { + AlphaFormat.Premul => SKAlphaType.Premul, + AlphaFormat.Unpremul => SKAlphaType.Unpremul, + AlphaFormat.Opaque => SKAlphaType.Opaque, + _ => throw new ArgumentException($"Unknown alpha format: {fmt}") + }; + } + + public static AlphaFormat ToAlphaFormat(this SKAlphaType fmt) + { + return fmt switch + { + SKAlphaType.Premul => AlphaFormat.Premul, + SKAlphaType.Unpremul => AlphaFormat.Unpremul, + SKAlphaType.Opaque => AlphaFormat.Opaque, + _ => throw new ArgumentException($"Unknown alpha format: {fmt}") + }; + } + public static SKShaderTileMode ToSKShaderTileMode(this Media.GradientSpreadMethod m) { switch (m) diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 9bdd30f40b..3c29bf65ed 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -22,12 +22,15 @@ namespace Avalonia.Skia /// 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) + /// The alpha format. + public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) { PixelSize = size; Dpi = dpi; var colorType = PixelFormatHelper.ResolveColorType(format); + + SKAlphaType alphaType = alphaFormat?.ToSkAlphaType() ?? SKAlphaType.Premul; var runtimePlatform = AvaloniaLocator.Current?.GetService(); @@ -35,14 +38,14 @@ namespace Avalonia.Skia { _bitmap = new SKBitmap(); - var nfo = new SKImageInfo(size.Width, size.Height, colorType, SKAlphaType.Premul); + var nfo = new SKImageInfo(size.Width, size.Height, colorType, alphaType); var blob = runtimePlatform.AllocBlob(nfo.BytesSize); _bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, s_releaseDelegate, blob); } else { - _bitmap = new SKBitmap(size.Width, size.Height, colorType, SKAlphaType.Premul); + _bitmap = new SKBitmap(size.Width, size.Height, colorType, alphaType); } _bitmap.Erase(SKColor.Empty); diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index c4c0541d53..047cd7a13b 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -115,11 +115,6 @@ namespace Avalonia.Direct2D1 SharpDX.Configuration.EnableReleaseOnFinalizer = true; } - public IBitmapImpl CreateBitmap(PixelSize size, Vector dpi) - { - return new WicBitmapImpl(size, dpi); - } - public IFormattedTextImpl CreateFormattedText( string text, Typeface typeface, @@ -171,9 +166,9 @@ namespace Avalonia.Direct2D1 return new WicRenderTargetBitmapImpl(size, dpi); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) { - return new WriteableWicBitmapImpl(size, dpi, format); + return new WriteableWicBitmapImpl(size, dpi, format, alphaFormat); } public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect); @@ -213,9 +208,9 @@ namespace Avalonia.Direct2D1 } /// - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { - return new WicBitmapImpl(format, data, size, dpi, stride); + return new WicBitmapImpl(format, alphaFormat, data, size, dpi, stride); } public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width) diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs index 743abddd1e..49193afd78 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs @@ -1,9 +1,9 @@ using System; using System.IO; -using System.Security.Cryptography; using Avalonia.Win32.Interop; using SharpDX.WIC; using APixelFormat = Avalonia.Platform.PixelFormat; +using AlphaFormat = Avalonia.Platform.AlphaFormat; using D2DBitmap = SharpDX.Direct2D1.Bitmap; namespace Avalonia.Direct2D1.Media @@ -73,29 +73,34 @@ namespace Avalonia.Direct2D1.Media /// The size of the bitmap in device pixels. /// The DPI of the bitmap. /// Pixel format - public WicBitmapImpl(PixelSize size, Vector dpi, APixelFormat? pixelFormat = null) + /// Alpha format. + public WicBitmapImpl(PixelSize size, Vector dpi, APixelFormat? pixelFormat = null, AlphaFormat? alphaFormat = null) { if (!pixelFormat.HasValue) { pixelFormat = APixelFormat.Bgra8888; } + if (!alphaFormat.HasValue) + { + alphaFormat = AlphaFormat.Premul; + } + PixelFormat = pixelFormat; WicImpl = new Bitmap( Direct2D1Platform.ImagingFactory, size.Width, size.Height, - pixelFormat.Value.ToWic(), + pixelFormat.Value.ToWic(alphaFormat.Value), BitmapCreateCacheOption.CacheOnLoad); Dpi = dpi; } - public WicBitmapImpl(APixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) + public WicBitmapImpl(APixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { - WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, size.Width, size.Height, format.ToWic(), BitmapCreateCacheOption.CacheOnDemand); + WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, size.Width, size.Height, format.ToWic(alphaFormat), BitmapCreateCacheOption.CacheOnDemand); WicImpl.SetResolution(dpi.X, dpi.Y); - PixelFormat = format; Dpi = dpi; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs index 4c906d62ad..3261c45f15 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs @@ -7,8 +7,8 @@ namespace Avalonia.Direct2D1.Media.Imaging { class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl { - public WriteableWicBitmapImpl(PixelSize size, Vector dpi, PixelFormat? pixelFormat) - : base(size, dpi, pixelFormat) + public WriteableWicBitmapImpl(PixelSize size, Vector dpi, PixelFormat? pixelFormat, AlphaFormat? alphaFormat) + : base(size, dpi, pixelFormat, alphaFormat) { } diff --git a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs index 18304ba211..31e9c260e0 100644 --- a/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs +++ b/src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Avalonia.Platform; using SharpDX; using SharpDX.Direct2D1; using SharpDX.Mathematics.Interop; @@ -89,14 +90,16 @@ namespace Avalonia.Direct2D1 return CapStyle.Triangle; } - public static Guid ToWic(this Platform.PixelFormat format) + public static Guid ToWic(this Platform.PixelFormat format, Platform.AlphaFormat alphaFormat) { + bool isPremul = alphaFormat == AlphaFormat.Premul; + if (format == Platform.PixelFormat.Rgb565) return SharpDX.WIC.PixelFormat.Format16bppBGR565; if (format == Platform.PixelFormat.Bgra8888) - return SharpDX.WIC.PixelFormat.Format32bppPBGRA; + return isPremul ? SharpDX.WIC.PixelFormat.Format32bppPBGRA : SharpDX.WIC.PixelFormat.Format32bppBGRA; if (format == Platform.PixelFormat.Rgba8888) - return SharpDX.WIC.PixelFormat.Format32bppPRGBA; + return isPremul ? SharpDX.WIC.PixelFormat.Format32bppPRGBA : SharpDX.WIC.PixelFormat.Format32bppRGBA; throw new ArgumentException("Unknown pixel format"); } From 77ef0ea144cb94ae5dc793037757dacf5aea3deb Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 11 Jul 2020 12:13:47 +0200 Subject: [PATCH 02/18] Fix test. --- src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs | 4 ++-- tests/Avalonia.Benchmarks/NullRenderingPlatform.cs | 4 ++-- tests/Avalonia.RenderTests/Media/BitmapTests.cs | 2 +- tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs | 4 +++- .../VisualTree/MockRenderInterface.cs | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index f01d335fc7..0a8bb10618 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -51,7 +51,7 @@ namespace Avalonia.Headless return new HeadlessBitmapStub(size, dpi); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) { return new HeadlessBitmapStub(size, dpi); } @@ -66,7 +66,7 @@ namespace Avalonia.Headless return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); } - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); } diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index d9fb5e03fc..e82ea08970 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -46,7 +46,7 @@ namespace Avalonia.Benchmarks throw new NotImplementedException(); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) { throw new NotImplementedException(); } @@ -61,7 +61,7 @@ namespace Avalonia.Benchmarks throw new NotImplementedException(); } - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs index 8773b85045..d52539c371 100644 --- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs +++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs @@ -81,7 +81,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media ctx.PopOpacity(); } - var bmp = new Bitmap(fmt, fb.Address, fb.Size, new Vector(96, 96), fb.RowBytes); + var bmp = new Bitmap(fmt, AlphaFormat.Premul, fb.Address, fb.Size, new Vector(96, 96), fb.RowBytes); fb.Deallocate(); using (var rtb = new RenderTargetBitmap(new PixelSize(100, 100), new Vector(96, 96))) { diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 5f2c6d1a69..c158dfbf64 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -55,7 +55,8 @@ namespace Avalonia.UnitTests public IWriteableBitmapImpl CreateWriteableBitmap( PixelSize size, Vector dpi, - PixelFormat? format = default(PixelFormat?)) + PixelFormat? format = null, + AlphaFormat? alphaFormat = null) { throw new NotImplementedException(); } @@ -87,6 +88,7 @@ namespace Avalonia.UnitTests public IBitmapImpl LoadBitmap( PixelFormat format, + AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs index 558a9a5968..ca25876ce7 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs @@ -47,7 +47,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree throw new NotImplementedException(); } - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { throw new NotImplementedException(); } @@ -64,7 +64,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree return new MockFontManagerImpl(); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? fmt) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? fmt, AlphaFormat? alphaFormat) { throw new NotImplementedException(); } From aff78fa6620f369f9a2ddf609a303fa7dbc23994 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 30 Jul 2020 23:29:04 +0200 Subject: [PATCH 03/18] Add default alpha and pixel formats to render interfaces. --- .../HeadlessPlatformRenderInterface.cs | 8 +++-- src/Avalonia.Visuals/Media/Imaging/Bitmap.cs | 18 +++++++++++ .../Media/Imaging/WriteableBitmap.cs | 32 ++++++++++++++++--- .../Platform/IPlatformRenderInterface.cs | 16 ++++++++-- .../Avalonia.Skia/PlatformRenderInterface.cs | 8 ++++- src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 7 ++-- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 6 +++- .../NullRenderingPlatform.cs | 6 +++- .../MockPlatformRenderInterface.cs | 8 +++-- .../VisualTree/MockRenderInterface.cs | 4 ++- 10 files changed, 94 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 0a8bb10618..3ae6c8c30e 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -21,7 +21,11 @@ namespace Avalonia.Headless public IEnumerable InstalledFontNames { get; } = new[] { "Tahoma" }; - public bool SupportsIndividualRoundRects => throw new NotImplementedException(); + public bool SupportsIndividualRoundRects => false; + + public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + + public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888; public IFormattedTextImpl CreateFormattedText(string text, Typeface typeface, double fontSize, TextAlignment textAlignment, TextWrapping wrapping, Size constraint, IReadOnlyList spans) { @@ -51,7 +55,7 @@ namespace Avalonia.Headless return new HeadlessBitmapStub(size, dpi); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) { return new HeadlessBitmapStub(size, dpi); } diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs index f86ce326d5..ca303211cd 100644 --- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs @@ -95,6 +95,24 @@ namespace Avalonia.Media.Imaging PlatformImpl.Dispose(); } + /// + /// Initializes a new instance of the class. + /// + /// The pixel format. + /// The alpha 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. + [Obsolete("Use overload taking an AlphaFormat.")] + public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) + { + var ri = AvaloniaLocator.Current.GetService(); + + PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService() + .LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride)); + } + /// /// Initializes a new instance of the class. /// diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs index b93706e6e5..b86444bc2f 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs @@ -1,4 +1,5 @@ -using Avalonia.Platform; +using System; +using Avalonia.Platform; namespace Avalonia.Media.Imaging { @@ -7,6 +8,19 @@ namespace Avalonia.Media.Imaging /// public class WriteableBitmap : Bitmap { + /// + /// 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 . + [Obsolete("Use overload taking an AlphaFormat.")] + public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) + : base(CreatePlatformImpl(size, dpi, format, null)) + { + } + /// /// Initializes a new instance of the class. /// @@ -15,11 +29,21 @@ namespace Avalonia.Media.Imaging /// The pixel format (optional). /// The alpha format (optional). /// An . - public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) - : base(AvaloniaLocator.Current.GetService().CreateWriteableBitmap(size, dpi, format, alphaFormat)) + public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) + : base(CreatePlatformImpl(size, dpi, format, alphaFormat)) { } - + public ILockedFramebuffer Lock() => ((IWriteableBitmapImpl) PlatformImpl.Item).Lock(); + + private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat) + { + var ri = AvaloniaLocator.Current.GetService(); + + PixelFormat finalFormat = format ?? ri.DefaultPixelFormat; + AlphaFormat finalAlphaFormat = alphaFormat ?? ri.DefaultAlphaFormat; + + return ri.CreateWriteableBitmap(size, dpi, finalFormat, finalAlphaFormat); + } } } diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index 5906d4080e..fb4a4427b7 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -81,10 +81,10 @@ namespace Avalonia.Platform /// /// The size of the bitmap in device pixels. /// The DPI of the bitmap. - /// Pixel format (optional). - /// Alpha format (optional). + /// Pixel format. + /// Alpha format . /// An . - IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null); + IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat); /// /// Loads a bitmap implementation from a file.. @@ -141,5 +141,15 @@ namespace Avalonia.Platform IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width); bool SupportsIndividualRoundRects { get; } + + /// + /// Default used on this platform. + /// + public AlphaFormat DefaultAlphaFormat { get; } + + /// + /// Default used on this platform. + /// + public PixelFormat DefaultPixelFormat { get; } } } diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 8c53ac2b4e..c4f70df7c0 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -22,6 +22,8 @@ namespace Avalonia.Skia public PlatformRenderInterface(ISkiaGpu skiaGpu, long? maxResourceBytes = null) { + DefaultPixelFormat = SKImageInfo.PlatformColorType.ToPixelFormat(); + if (skiaGpu != null) { _skiaGpu = skiaGpu; @@ -150,7 +152,7 @@ namespace Avalonia.Skia } /// - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) { return new WriteableBitmapImpl(size, dpi, format, alphaFormat); } @@ -265,5 +267,9 @@ namespace Avalonia.Skia } public bool SupportsIndividualRoundRects => true; + + public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + + public PixelFormat DefaultPixelFormat { get; } } } diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 3c29bf65ed..d48e7d10e6 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -23,14 +23,13 @@ namespace Avalonia.Skia /// The DPI of the bitmap. /// The pixel format. /// The alpha format. - public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) + public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) { PixelSize = size; Dpi = dpi; - var colorType = PixelFormatHelper.ResolveColorType(format); - - SKAlphaType alphaType = alphaFormat?.ToSkAlphaType() ?? SKAlphaType.Premul; + SKColorType colorType = format.ToSkColorType(); + SKAlphaType alphaType = alphaFormat.ToSkAlphaType(); var runtimePlatform = AvaloniaLocator.Current?.GetService(); diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 047cd7a13b..ae927d44a5 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -166,7 +166,7 @@ namespace Avalonia.Direct2D1 return new WicRenderTargetBitmapImpl(size, dpi); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) { return new WriteableWicBitmapImpl(size, dpi, format, alphaFormat); } @@ -261,5 +261,9 @@ namespace Avalonia.Direct2D1 } public bool SupportsIndividualRoundRects => false; + + public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + + public PixelFormat DefaultPixelFormat => PixelFormat.Bgra8888; } } diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index e82ea08970..f632d85c26 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -46,7 +46,7 @@ namespace Avalonia.Benchmarks throw new NotImplementedException(); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) { throw new NotImplementedException(); } @@ -94,5 +94,9 @@ namespace Avalonia.Benchmarks } public bool SupportsIndividualRoundRects => true; + + public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + + public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888; } } diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index c158dfbf64..08df23cbe1 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -55,8 +55,8 @@ namespace Avalonia.UnitTests public IWriteableBitmapImpl CreateWriteableBitmap( PixelSize size, Vector dpi, - PixelFormat? format = null, - AlphaFormat? alphaFormat = null) + PixelFormat format, + AlphaFormat alphaFormat) { throw new NotImplementedException(); } @@ -104,5 +104,9 @@ namespace Avalonia.UnitTests } public bool SupportsIndividualRoundRects { get; set; } + + public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + + public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888; } } diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs index ca25876ce7..02236e4107 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs @@ -58,13 +58,15 @@ namespace Avalonia.Visuals.UnitTests.VisualTree } public bool SupportsIndividualRoundRects { get; set; } + public AlphaFormat DefaultAlphaFormat { get; } + public PixelFormat DefaultPixelFormat { get; } public IFontManagerImpl CreateFontManager() { return new MockFontManagerImpl(); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? fmt, AlphaFormat? alphaFormat) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat fmt, AlphaFormat alphaFormat) { throw new NotImplementedException(); } From 776ffbcbdcb6eaa8e32f989a7527bc8a201362dc Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 30 Jul 2020 23:34:09 +0200 Subject: [PATCH 04/18] Add documentation for alpha format. --- src/Avalonia.Visuals/Platform/AlphaFormat.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Avalonia.Visuals/Platform/AlphaFormat.cs b/src/Avalonia.Visuals/Platform/AlphaFormat.cs index ce8dcde728..3ef8fe9520 100644 --- a/src/Avalonia.Visuals/Platform/AlphaFormat.cs +++ b/src/Avalonia.Visuals/Platform/AlphaFormat.cs @@ -1,9 +1,21 @@ namespace Avalonia.Platform { + /// + /// Describes how to interpret the alpha component of a pixel. + /// public enum AlphaFormat { + /// + /// All pixels have their alpha premultiplied in their color components. + /// Premul, + /// + /// All pixels have their color components stored without any regard to the alpha. e.g. this is the default configuration for PNG images. + /// Unpremul, + /// + /// All pixels are stored as opaque. + /// Opaque } } From 3fc31a0dd5e38d41cded7f83cbc90654cec33af6 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 30 Jul 2020 23:37:46 +0200 Subject: [PATCH 05/18] Generate api compat baseline. --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 00618448a2..849e957a72 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -1,4 +1,14 @@ Compat issues with assembly Avalonia.Visuals: EnumValuesMustMatch : Enum value 'Avalonia.Media.FontStyle Avalonia.Media.FontStyle.Italic' is (System.Int32)1 in the implementation but (System.Int32)2 in the contract. EnumValuesMustMatch : Enum value 'Avalonia.Media.FontStyle Avalonia.Media.FontStyle.Oblique' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. -Total Issues: 2 +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.AlphaFormat Avalonia.Platform.IPlatformRenderInterface.DefaultAlphaFormat' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.PixelFormat Avalonia.Platform.IPlatformRenderInterface.DefaultPixelFormat' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, Avalonia.Platform.PixelFormat, Avalonia.Platform.AlphaFormat)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, System.Nullable)' is present in the contract but not in the implementation. +MembersMustExist : Member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.CreateWriteableBitmap(Avalonia.PixelSize, Avalonia.Vector, System.Nullable)' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.AlphaFormat Avalonia.Platform.IPlatformRenderInterface.DefaultAlphaFormat.get()' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.PixelFormat Avalonia.Platform.IPlatformRenderInterface.DefaultPixelFormat.get()' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, Avalonia.Platform.AlphaFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' is present in the contract but not in the implementation. +MembersMustExist : Member 'public Avalonia.Platform.IBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadBitmap(Avalonia.Platform.PixelFormat, System.IntPtr, Avalonia.PixelSize, Avalonia.Vector, System.Int32)' does not exist in the implementation but it does exist in the contract. +Total Issues: 12 From 15e78765308e42716332c4c737272e775509ebec Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 6 Aug 2020 16:42:51 +0200 Subject: [PATCH 06/18] Fix incorrect argument ordering. --- .../Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs b/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs index bf1b80f947..9f1c32bf64 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs @@ -34,7 +34,7 @@ namespace Avalonia.Controls /// public int OldIndex { get; private set; } - internal void Update(IControl element, int newIndex, int oldIndex) + internal void Update(IControl element, int oldIndex, int newIndex) { Element = element; NewIndex = newIndex; From 472352558f24c7be59c9bfd86a6129d600ae6e82 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 6 Aug 2020 17:13:34 +0200 Subject: [PATCH 07/18] Added failing test for #4403. --- tests/Avalonia.Styling.UnitTests/SetterTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Avalonia.Styling.UnitTests/SetterTests.cs b/tests/Avalonia.Styling.UnitTests/SetterTests.cs index 46cde30b8a..901233801e 100644 --- a/tests/Avalonia.Styling.UnitTests/SetterTests.cs +++ b/tests/Avalonia.Styling.UnitTests/SetterTests.cs @@ -35,6 +35,21 @@ namespace Avalonia.Styling.UnitTests Assert.Equal("foo", control.Text); } + [Fact] + public void Setter_Should_Handle_Binding_Producing_UnsetValue() + { + var control = new TextBlock(); + var subject = new BehaviorSubject(AvaloniaProperty.UnsetValue); + var descriptor = InstancedBinding.OneWay(subject); + var binding = Mock.Of(x => x.Initiate(control, TextBlock.TagProperty, null, false) == descriptor); + var style = Mock.Of(); + var setter = new Setter(TextBlock.TagProperty, binding); + + setter.Instance(control).Start(false); + + Assert.Equal("", control.Text); + } + [Fact] public void Setter_Should_Materialize_Template_To_Property() { From de8171f8ac2221095bd9129636d754194992884b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 6 Aug 2020 17:13:49 +0200 Subject: [PATCH 08/18] Correctly handle UnsetValue/DoNothing in setters. Just need to call `BindingValue.FromUntyped` instead of doing a conditional, because otherwise we run the risk of passing `UnsetValue` to the `BindingValue` ctor which will throw in that case. --- src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs b/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs index e177993d13..929f7142bb 100644 --- a/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs +++ b/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs @@ -164,7 +164,7 @@ namespace Avalonia.Styling private void ConvertAndPublishNext(object? value) { - _value = value is T v ? v : BindingValue.FromUntyped(value); + _value = BindingValue.FromUntyped(value); if (_isActive) { From e74a0d9698e1937ca46b17a330e7669ca041c3f8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 7 Aug 2020 14:25:31 +0200 Subject: [PATCH 09/18] Make ItemsRepeater work with no ScrollViewer. There doesn't seem to be any reason to make `ItemsRepeater` not work properly when there's no `ScrollViewer`. Fixes #4330 --- src/Avalonia.Controls/Repeater/ViewportManager.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs index 0d22187b34..796ae8616d 100644 --- a/src/Avalonia.Controls/Repeater/ViewportManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs @@ -469,13 +469,7 @@ namespace Avalonia.Controls parent = parent.VisualParent; } - if (_scroller == null) - { - // We usually update the viewport in the post arrange handler. But, since we don't have - // a scroller, let's do it now. - UpdateViewport(Rect.Empty); - } - else if (!_managingViewportDisabled) + if (!_managingViewportDisabled) { _owner.EffectiveViewportChanged += OnEffectiveViewportChanged; _effectiveViewportChangedSubscribed = true; From 2337da31b1367b57c61ff70b1489f7817d31be6b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 7 Aug 2020 15:17:18 +0200 Subject: [PATCH 10/18] Add null check for scroller. --- src/Avalonia.Controls/Repeater/ViewportManager.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs index 796ae8616d..bdb0fa3270 100644 --- a/src/Avalonia.Controls/Repeater/ViewportManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs @@ -350,11 +350,14 @@ namespace Avalonia.Controls } // Make sure that only the target child can be the anchor during the bring into view operation. - foreach (var child in _owner.Children) + if (_scroller is object) { - if (child != targetChild) + foreach (var child in _owner.Children) { - _scroller.UnregisterAnchorCandidate(child); + if (child != targetChild) + { + _scroller.UnregisterAnchorCandidate(child); + } } } From aed62ca4a71bb6e77d37035b97b4c9aacde63b3a Mon Sep 17 00:00:00 2001 From: Maksym Katsydan Date: Fri, 7 Aug 2020 16:02:37 -0400 Subject: [PATCH 11/18] Fluent: Add missing MenuItem Header styles --- samples/ControlCatalog/Pages/MenuPage.xaml | 4 ++++ src/Avalonia.Themes.Fluent/MenuItem.xaml | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml index e9d2301e89..45564f3e41 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml +++ b/samples/ControlCatalog/Pages/MenuPage.xaml @@ -17,12 +17,16 @@ + + + + diff --git a/src/Avalonia.Themes.Fluent/MenuItem.xaml b/src/Avalonia.Themes.Fluent/MenuItem.xaml index 2115319c3c..3a03ec1acf 100644 --- a/src/Avalonia.Themes.Fluent/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/MenuItem.xaml @@ -8,6 +8,8 @@ Height="200"> + @@ -83,7 +85,6 @@ Content="{TemplateBinding Header}" VerticalAlignment="Center" HorizontalAlignment="Stretch" - TextBlock.Foreground="{TemplateBinding Foreground}" Grid.Column="1"> @@ -213,6 +214,9 @@ + @@ -224,6 +228,9 @@ + @@ -231,9 +238,12 @@ - + From 63a4333832300f4d823f1bb6da755d1f017ffeda Mon Sep 17 00:00:00 2001 From: Maksym Katsydan Date: Fri, 7 Aug 2020 16:53:29 -0400 Subject: [PATCH 12/18] ControlCatalog: Update ContextMenuPage and MenuPage samples --- samples/ControlCatalog/App.xaml | 2 +- samples/ControlCatalog/Pages/ContextMenuPage.xaml | 5 +++-- samples/ControlCatalog/Pages/MenuPage.xaml | 4 ++-- src/Avalonia.Themes.Fluent/ContextMenu.xaml | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index 1c8c38d3e5..bab57f3544 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -11,7 +11,7 @@ diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml b/samples/ControlCatalog/Pages/ContextMenuPage.xaml index 8ccd8e97f7..260162ddb9 100644 --- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml +++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml @@ -14,13 +14,14 @@ Padding="48,48,48,48"> - + + - + diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml index 45564f3e41..2c09cb9b4d 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml +++ b/samples/ControlCatalog/Pages/MenuPage.xaml @@ -16,8 +16,8 @@ Defined in XAML - - + + diff --git a/src/Avalonia.Themes.Fluent/ContextMenu.xaml b/src/Avalonia.Themes.Fluent/ContextMenu.xaml index a6b6156944..6ad038676b 100644 --- a/src/Avalonia.Themes.Fluent/ContextMenu.xaml +++ b/src/Avalonia.Themes.Fluent/ContextMenu.xaml @@ -9,6 +9,8 @@ + From 09b042683a51adbb7db402fefa3d8f86ac77edc9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 10 Aug 2020 15:17:54 +0200 Subject: [PATCH 13/18] Move some tests. Tests for `AvaloniaObject.IsSet` were in the `Metadata` test class, though `IsSet` has nothing to do with metadata. Moved them into `SetValue` which is more relevant. --- .../AvaloniaObjectTests_Metadata.cs | 29 ------------------- .../AvaloniaObjectTests_SetValue.cs | 29 +++++++++++++++++++ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs index 8d04f817f1..c3f7b816f2 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs @@ -15,35 +15,6 @@ namespace Avalonia.Base.UnitTests p = AttachedOwner.AttachedProperty; } - [Fact] - public void IsSet_Returns_False_For_Unset_Property() - { - var target = new Class1(); - - Assert.False(target.IsSet(Class1.FooProperty)); - } - - [Fact] - public void IsSet_Returns_False_For_Set_Property() - { - var target = new Class1(); - - target.SetValue(Class1.FooProperty, "foo"); - - Assert.True(target.IsSet(Class1.FooProperty)); - } - - [Fact] - public void IsSet_Returns_False_For_Cleared_Property() - { - var target = new Class1(); - - target.SetValue(Class1.FooProperty, "foo"); - target.SetValue(Class1.FooProperty, AvaloniaProperty.UnsetValue); - - Assert.False(target.IsSet(Class1.FooProperty)); - } - private class Class1 : AvaloniaObject { public static readonly StyledProperty FooProperty = diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs index 66a741e122..954a609315 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs @@ -39,6 +39,35 @@ namespace Avalonia.Base.UnitTests Assert.Equal(1, raised); } + [Fact] + public void IsSet_Returns_False_For_Unset_Property() + { + var target = new Class1(); + + Assert.False(target.IsSet(Class1.FooProperty)); + } + + [Fact] + public void IsSet_Returns_False_For_Set_Property() + { + var target = new Class1(); + + target.SetValue(Class1.FooProperty, "foo"); + + Assert.True(target.IsSet(Class1.FooProperty)); + } + + [Fact] + public void IsSet_Returns_False_For_Cleared_Property() + { + var target = new Class1(); + + target.SetValue(Class1.FooProperty, "foo"); + target.SetValue(Class1.FooProperty, AvaloniaProperty.UnsetValue); + + Assert.False(target.IsSet(Class1.FooProperty)); + } + [Fact] public void SetValue_Sets_Value() { From 7d2563de3a882543d8f2a497fa5b9bee7afec56e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 10 Aug 2020 15:46:04 +0200 Subject: [PATCH 14/18] Expose OverrideMetadata for direct properties. --- src/Avalonia.Base/DirectPropertyBase.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index d42c030245..334b90177b 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -102,6 +102,26 @@ namespace Avalonia return (DirectPropertyMetadata)base.GetMetadata(type); } + /// + /// Overrides the metadata for the property on the specified type. + /// + /// The type. + /// The metadata. + public void OverrideMetadata(DirectPropertyMetadata metadata) where T : IAvaloniaObject + { + base.OverrideMetadata(typeof(T), metadata); + } + + /// + /// Overrides the metadata for the property on the specified type. + /// + /// The type. + /// The metadata. + public void OverrideMetadata(Type type, DirectPropertyMetadata metadata) + { + base.OverrideMetadata(type, metadata); + } + /// public override void Accept(IAvaloniaPropertyVisitor vistor, ref TData data) { From 7519f857062672281341d3e733e08ca0213e6ef0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 10 Aug 2020 16:23:04 +0200 Subject: [PATCH 15/18] Added property metadata tests. --- .../AvaloniaObjectTests_Metadata.cs | 98 ++++++++++++++++--- 1 file changed, 85 insertions(+), 13 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs index c3f7b816f2..161911dfd5 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Reactive.Linq; +using System.Runtime.CompilerServices; using Xunit; namespace Avalonia.Base.UnitTests @@ -9,28 +8,101 @@ namespace Avalonia.Base.UnitTests public AvaloniaObjectTests_Metadata() { // Ensure properties are registered. - AvaloniaProperty p; - p = Class1.FooProperty; - p = Class2.BarProperty; - p = AttachedOwner.AttachedProperty; + RuntimeHelpers.RunClassConstructor(typeof(Class1).TypeHandle); + RuntimeHelpers.RunClassConstructor(typeof(Class2).TypeHandle); + RuntimeHelpers.RunClassConstructor(typeof(Class3).TypeHandle); + } + + public class StyledProperty : AvaloniaObjectTests_Metadata + { + [Fact] + public void Default_Value_Can_Be_Overridden_In_Derived_Class() + { + var baseValue = Class1.StyledProperty.GetDefaultValue(typeof(Class1)); + var derivedValue = Class1.StyledProperty.GetDefaultValue(typeof(Class2)); + + Assert.Equal("foo", baseValue); + Assert.Equal("bar", derivedValue); + } + + [Fact] + public void Default_Value_Can_Be_Overridden_In_AddOwnered_Property() + { + var baseValue = Class1.StyledProperty.GetDefaultValue(typeof(Class1)); + var addOwneredValue = Class1.StyledProperty.GetDefaultValue(typeof(Class3)); + + Assert.Equal("foo", baseValue); + Assert.Equal("baz", addOwneredValue); + } + } + + public class DirectProperty : AvaloniaObjectTests_Metadata + { + [Fact] + public void Unset_Value_Can_Be_Overridden_In_Derived_Class() + { + var baseValue = Class1.DirectProperty.GetUnsetValue(typeof(Class1)); + var derivedValue = Class1.DirectProperty.GetUnsetValue(typeof(Class2)); + + Assert.Equal("foo", baseValue); + Assert.Equal("bar", derivedValue); + } + + [Fact] + public void Unset_Value_Can_Be_Overridden_In_AddOwnered_Property() + { + var baseValue = Class1.DirectProperty.GetUnsetValue(typeof(Class1)); + var addOwneredValue = Class3.DirectProperty.GetUnsetValue(typeof(Class3)); + + Assert.Equal("foo", baseValue); + Assert.Equal("baz", addOwneredValue); + } } private class Class1 : AvaloniaObject { - public static readonly StyledProperty FooProperty = - AvaloniaProperty.Register("Foo"); + public static readonly StyledProperty StyledProperty = + AvaloniaProperty.Register("Styled", "foo"); + + public static readonly DirectProperty DirectProperty = + AvaloniaProperty.RegisterDirect("Styled", o => o.Direct, unsetValue: "foo"); + + private string _direct; + + public string Direct + { + get => _direct; + } } private class Class2 : Class1 { - public static readonly StyledProperty BarProperty = - AvaloniaProperty.Register("Bar"); + static Class2() + { + StyledProperty.OverrideDefaultValue("bar"); + DirectProperty.OverrideMetadata(new DirectPropertyMetadata("bar")); + } } - private class AttachedOwner + private class Class3 : AvaloniaObject { - public static readonly AttachedProperty AttachedProperty = - AvaloniaProperty.RegisterAttached("Attached"); + public static readonly StyledProperty StyledProperty = + Class1.StyledProperty.AddOwner(); + + public static readonly DirectProperty DirectProperty = + Class1.DirectProperty.AddOwner(o => o.Direct, unsetValue: "baz"); + + private string _direct; + + static Class3() + { + StyledProperty.OverrideDefaultValue("baz"); + } + + public string Direct + { + get => _direct; + } } } } From d5716fce8f24aeebad0485b45abe4dedeb3ee53d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 11 Aug 2020 15:30:36 +0200 Subject: [PATCH 16/18] Added failing data validation test. --- .../AvaloniaObjectTests_DataValidation.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs index e8cc71c723..65f03b3eca 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs @@ -62,6 +62,20 @@ namespace Avalonia.Base.UnitTests Assert.Equal(7, result[3].Value); } + [Fact] + public void Binding_Overridden_Validated_Direct_Property_Calls_UpdateDataValidation() + { + var target = new Class2(); + var source = new Subject>(); + + // Class2 overrides `NonValidatedDirectProperty`'s metadata to enable data validation. + target.Bind(Class1.NonValidatedDirectProperty, source); + source.OnNext(1); + + var result = target.Notifications.Cast>().ToList(); + Assert.Equal(1, result.Count); + } + [Fact] public void Bound_Validated_Direct_String_Property_Can_Be_Set_To_Null() { @@ -150,6 +164,15 @@ namespace Avalonia.Base.UnitTests } } + private class Class2 : Class1 + { + static Class2() + { + NonValidatedDirectProperty.OverrideMetadata( + new DirectPropertyMetadata(enableDataValidation: true)); + } + } + public class ViewModel : NotifyingBase { private string _stringValue; From 37bd384794174e273ffd8706b1c157792bc0607c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 11 Aug 2020 15:39:23 +0200 Subject: [PATCH 17/18] Use EnabledDataValidation from metadata. `DirectProperty` stored a `EnabledDataValidation` flag in its metadata but also had an `IsDataValidationEnabled` property in `DirectPropertyBase` which was the one used by `AvaloniaObject`. Use the version from metadata so that it can be overridden and remove the flag from `DirectPropertyBase`. --- src/Avalonia.Base/AvaloniaObject.cs | 4 ++- src/Avalonia.Base/AvaloniaProperty.cs | 6 ++--- src/Avalonia.Base/DirectProperty.cs | 25 ++++++------------- src/Avalonia.Base/DirectPropertyBase.cs | 19 ++------------ .../AvaloniaObjectTests_Direct.cs | 6 ++--- .../DirectPropertyTests.cs | 3 +-- 6 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index d18f0b3f94..65233f9230 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -806,7 +806,9 @@ namespace Avalonia break; } - if (p.IsDataValidationEnabled) + var metadata = p.GetMetadata(GetType()); + + if (metadata.EnableDataValidation == true) { UpdateDataValidation(property, value); } diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index a873d5fd42..39391490b0 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -369,14 +369,14 @@ namespace Avalonia var metadata = new DirectPropertyMetadata( unsetValue: unsetValue, - defaultBindingMode: defaultBindingMode); + defaultBindingMode: defaultBindingMode, + enableDataValidation: enableDataValidation); var result = new DirectProperty( name, getter, setter, - metadata, - enableDataValidation); + metadata); AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result); return result; } diff --git a/src/Avalonia.Base/DirectProperty.cs b/src/Avalonia.Base/DirectProperty.cs index d21969502a..a8120fbd4f 100644 --- a/src/Avalonia.Base/DirectProperty.cs +++ b/src/Avalonia.Base/DirectProperty.cs @@ -23,16 +23,12 @@ namespace Avalonia /// Gets the current value of the property. /// Sets the value of the property. May be null. /// The property metadata. - /// - /// Whether the property is interested in data validation. - /// public DirectProperty( string name, Func getter, Action setter, - DirectPropertyMetadata metadata, - bool enableDataValidation) - : base(name, typeof(TOwner), metadata, enableDataValidation) + DirectPropertyMetadata metadata) + : base(name, typeof(TOwner), metadata) { Contract.Requires(getter != null); @@ -47,16 +43,12 @@ namespace Avalonia /// Gets the current value of the property. /// Sets the value of the property. May be null. /// Optional overridden metadata. - /// - /// Whether the property is interested in data validation. - /// private DirectProperty( DirectPropertyBase source, Func getter, Action setter, - DirectPropertyMetadata metadata, - bool enableDataValidation) - : base(source, typeof(TOwner), metadata, enableDataValidation) + DirectPropertyMetadata metadata) + : base(source, typeof(TOwner), metadata) { Contract.Requires(getter != null); @@ -107,7 +99,8 @@ namespace Avalonia { var metadata = new DirectPropertyMetadata( unsetValue: unsetValue, - defaultBindingMode: defaultBindingMode); + defaultBindingMode: defaultBindingMode, + enableDataValidation: enableDataValidation); metadata.Merge(GetMetadata(), this); @@ -115,8 +108,7 @@ namespace Avalonia (DirectPropertyBase)this, getter, setter, - metadata, - enableDataValidation); + metadata); AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result); return result; @@ -155,8 +147,7 @@ namespace Avalonia this, getter, setter, - metadata, - enableDataValidation); + metadata); AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result); return result; diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 334b90177b..dbc2625b86 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -23,17 +23,12 @@ namespace Avalonia /// The name of the property. /// The type of the class that registers the property. /// The property metadata. - /// - /// Whether the property is interested in data validation. - /// protected DirectPropertyBase( string name, Type ownerType, - PropertyMetadata metadata, - bool enableDataValidation) + PropertyMetadata metadata) : base(name, ownerType, metadata) { - IsDataValidationEnabled = enableDataValidation; } /// @@ -42,17 +37,12 @@ namespace Avalonia /// The property to copy. /// The new owner type. /// Optional overridden metadata. - /// - /// Whether the property is interested in data validation. - /// protected DirectPropertyBase( AvaloniaProperty source, Type ownerType, - PropertyMetadata metadata, - bool enableDataValidation) + PropertyMetadata metadata) : base(source, ownerType, metadata) { - IsDataValidationEnabled = enableDataValidation; } /// @@ -60,11 +50,6 @@ namespace Avalonia /// public abstract Type Owner { get; } - /// - /// Gets a value that indicates whether data validation is enabled for the property. - /// - public bool IsDataValidationEnabled { get; } - /// /// Gets the value of the property on the instance. /// diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs index 60d06b359a..81a8de1046 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs @@ -547,8 +547,7 @@ namespace Avalonia.Base.UnitTests "foo", o => "foo", null, - new DirectPropertyMetadata(defaultBindingMode: BindingMode.TwoWay), - false); + new DirectPropertyMetadata(defaultBindingMode: BindingMode.TwoWay)); var bar = foo.AddOwner(o => "bar"); Assert.Equal(BindingMode.TwoWay, bar.GetMetadata().DefaultBindingMode); @@ -562,8 +561,7 @@ namespace Avalonia.Base.UnitTests "foo", o => "foo", null, - new DirectPropertyMetadata(defaultBindingMode: BindingMode.TwoWay), - false); + new DirectPropertyMetadata(defaultBindingMode: BindingMode.TwoWay)); var bar = foo.AddOwner(o => "bar", defaultBindingMode: BindingMode.OneWayToSource); Assert.Equal(BindingMode.TwoWay, bar.GetMetadata().DefaultBindingMode); diff --git a/tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs b/tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs index 08b62ae100..e7e3b5764f 100644 --- a/tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs +++ b/tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs @@ -11,8 +11,7 @@ namespace Avalonia.Base.UnitTests "test", o => null, null, - new DirectPropertyMetadata(), - false); + new DirectPropertyMetadata()); Assert.True(target.IsDirect); } From edd20a414172c3a6318fa7b8a8b3ffe35daa6939 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 11 Aug 2020 15:42:07 +0200 Subject: [PATCH 18/18] Added API changes to ApiCompatBaseline. --- src/Avalonia.Base/ApiCompatBaseline.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/Avalonia.Base/ApiCompatBaseline.txt diff --git a/src/Avalonia.Base/ApiCompatBaseline.txt b/src/Avalonia.Base/ApiCompatBaseline.txt new file mode 100644 index 0000000000..b0b7371cd7 --- /dev/null +++ b/src/Avalonia.Base/ApiCompatBaseline.txt @@ -0,0 +1,6 @@ +Compat issues with assembly Avalonia.Base: +MembersMustExist : Member 'public void Avalonia.DirectProperty..ctor(System.String, System.Func, System.Action, Avalonia.DirectPropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase..ctor(Avalonia.AvaloniaProperty, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase..ctor(System.String, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Boolean Avalonia.DirectPropertyBase.IsDataValidationEnabled.get()' does not exist in the implementation but it does exist in the contract. +Total Issues: 4