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.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;
diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs
index 0d22187b34..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);
+ }
}
}
@@ -469,13 +472,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;
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index d8e7f3a387..317b6d3f2e 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -652,8 +652,8 @@ namespace Avalonia.Controls
PlatformImpl?.Show();
Renderer?.Start();
+ SetWindowStartupLocation(Owner?.PlatformImpl);
}
- SetWindowStartupLocation(Owner?.PlatformImpl);
OnOpened(EventArgs.Empty);
}
diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs
index eb6e7319f5..1efd6c8c1d 100644
--- a/src/Avalonia.Controls/WindowBase.cs
+++ b/src/Avalonia.Controls/WindowBase.cs
@@ -39,6 +39,7 @@ namespace Avalonia.Controls
public static readonly StyledProperty TopmostProperty =
AvaloniaProperty.Register(nameof(Topmost));
+ private int _autoSizing;
private bool _hasExecutedInitialLayoutPass;
private bool _isActive;
private bool _ignoreVisibilityChange;
@@ -97,11 +98,7 @@ namespace Avalonia.Controls
///
/// Whether an auto-size operation is in progress.
///
- protected bool AutoSizing
- {
- get;
- private set;
- }
+ protected bool AutoSizing => _autoSizing > 0;
///
/// Gets or sets the owner of the window.
@@ -186,8 +183,8 @@ namespace Avalonia.Controls
///
protected IDisposable BeginAutoSizing()
{
- AutoSizing = true;
- return Disposable.Create(() => AutoSizing = false);
+ ++_autoSizing;
+ return Disposable.Create(() => --_autoSizing);
}
///
diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index f01d335fc7..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)
+ public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
return new HeadlessBitmapStub(size, dpi);
}
@@ -66,7 +70,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/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)
{
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
diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
index 86e2700c04..ca303211cd 100644
--- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
+++ b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
@@ -99,14 +99,33 @@ 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.
+ [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.
+ ///
+ /// 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, 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..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
{
@@ -14,11 +15,35 @@ namespace Avalonia.Media.Imaging
/// The DPI of the bitmap.
/// The pixel format (optional).
/// An .
- public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
- : base(AvaloniaLocator.Current.GetService().CreateWriteableBitmap(size, dpi, format))
+ [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.
+ ///
+ /// 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, 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/AlphaFormat.cs b/src/Avalonia.Visuals/Platform/AlphaFormat.cs
new file mode 100644
index 0000000000..3ef8fe9520
--- /dev/null
+++ b/src/Avalonia.Visuals/Platform/AlphaFormat.cs
@@ -0,0 +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
+ }
+}
diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
index ba30272b7b..fb4a4427b7 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
@@ -82,9 +81,10 @@ namespace Avalonia.Platform
///
/// The size of the bitmap in device pixels.
/// The DPI of the bitmap.
- /// Pixel format (optional).
+ /// Pixel format.
+ /// Alpha format .
/// An .
- IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null);
+ IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat);
///
/// 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.
@@ -140,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/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..c4f70df7c0 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;
@@ -24,6 +22,8 @@ namespace Avalonia.Skia
public PlatformRenderInterface(ISkiaGpu skiaGpu, long? maxResourceBytes = null)
{
+ DefaultPixelFormat = SKImageInfo.PlatformColorType.ToPixelFormat();
+
if (skiaGpu != null)
{
_skiaGpu = skiaGpu;
@@ -76,9 +76,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 +152,9 @@ namespace Avalonia.Skia
}
///
- public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
+ public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
- return new WriteableBitmapImpl(size, dpi, format);
+ return new WriteableBitmapImpl(size, dpi, format, alphaFormat);
}
private static readonly SKFont s_font = new SKFont
@@ -267,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/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..d48e7d10e6 100644
--- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -22,12 +22,14 @@ 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, AlphaFormat alphaFormat)
{
PixelSize = size;
Dpi = dpi;
- var colorType = PixelFormatHelper.ResolveColorType(format);
+ SKColorType colorType = format.ToSkColorType();
+ SKAlphaType alphaType = alphaFormat.ToSkAlphaType();
var runtimePlatform = AvaloniaLocator.Current?.GetService();
@@ -35,14 +37,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..ae927d44a5 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, AlphaFormat alphaFormat)
{
- 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)
@@ -266,5 +261,9 @@ namespace Avalonia.Direct2D1
}
public bool SupportsIndividualRoundRects => false;
+
+ public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
+
+ public PixelFormat DefaultPixelFormat => PixelFormat.Bgra8888;
}
}
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");
}
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 6f22f94056..ddc0cc4e42 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -6,7 +6,6 @@ using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
-using Avalonia.Media;
using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
@@ -230,28 +229,104 @@ namespace Avalonia.Win32
private WindowTransparencyLevel EnableBlur(WindowTransparencyLevel transparencyLevel)
{
- bool canUseTransparency = false;
- bool canUseAcrylic = false;
-
- if (Win32Platform.WindowsVersion.Major >= 10)
+ if (Win32Platform.WindowsVersion.Major >= 6)
{
- canUseTransparency = true;
-
- if (Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628)
+ if (DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled)
+ {
+ return WindowTransparencyLevel.None;
+ }
+ else if (Win32Platform.WindowsVersion.Major >= 10)
+ {
+ return Win10EnableBlur(transparencyLevel);
+ }
+ else if (Win32Platform.WindowsVersion.Minor >= 2)
+ {
+ return Win8xEnableBlur(transparencyLevel);
+ }
+ else
{
- canUseAcrylic = true;
+ return Win7EnableBlur(transparencyLevel);
}
}
+ else
+ {
+ return WindowTransparencyLevel.None;
+ }
+ }
- if (!canUseTransparency || DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled)
+ private WindowTransparencyLevel Win7EnableBlur(WindowTransparencyLevel transparencyLevel)
+ {
+ if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
+ {
+ transparencyLevel = WindowTransparencyLevel.Blur;
+ }
+
+ var blurInfo = new DWM_BLURBEHIND(false);
+
+ if (transparencyLevel == WindowTransparencyLevel.Blur)
+ {
+ blurInfo = new DWM_BLURBEHIND(true);
+ }
+
+ DwmEnableBlurBehindWindow(_hwnd, ref blurInfo);
+
+ if (transparencyLevel == WindowTransparencyLevel.Transparent)
{
return WindowTransparencyLevel.None;
}
+ else
+ {
+ return transparencyLevel;
+ }
+ }
+
+ private WindowTransparencyLevel Win8xEnableBlur(WindowTransparencyLevel transparencyLevel)
+ {
+ var accent = new AccentPolicy();
+ var accentStructSize = Marshal.SizeOf(accent);
+
+ if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
+ {
+ transparencyLevel = WindowTransparencyLevel.Blur;
+ }
+
+ if (transparencyLevel == WindowTransparencyLevel.Transparent)
+ {
+ accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
+ }
+ else
+ {
+ accent.AccentState = AccentState.ACCENT_DISABLED;
+ }
+
+ var accentPtr = Marshal.AllocHGlobal(accentStructSize);
+ Marshal.StructureToPtr(accent, accentPtr, false);
+
+ var data = new WindowCompositionAttributeData();
+ data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
+ data.SizeOfData = accentStructSize;
+ data.Data = accentPtr;
+
+ SetWindowCompositionAttribute(_hwnd, ref data);
+
+ Marshal.FreeHGlobal(accentPtr);
+
+ if (transparencyLevel >= WindowTransparencyLevel.Blur)
+ {
+ Win7EnableBlur(transparencyLevel);
+ }
+
+ return transparencyLevel;
+ }
+
+ private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transparencyLevel)
+ {
+ bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
- if(transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
+ if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
{
transparencyLevel = WindowTransparencyLevel.Blur;
}
@@ -291,7 +366,7 @@ namespace Avalonia.Win32
SetWindowCompositionAttribute(_hwnd, ref data);
- Marshal.FreeHGlobal(accentPtr);
+ Marshal.FreeHGlobal(accentPtr);
return transparencyLevel;
}
diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
index d9fb5e03fc..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)
+ public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
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();
}
@@ -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.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.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