Browse Source

Merge branch 'master' into old-win-window-transparency

danwalmsley 5 years ago
parent
commit
093e3bf2ba
39 changed files with 502 additions and 148 deletions
  1. 1 1
      samples/ControlCatalog/App.xaml
  2. 3 2
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  3. 5 1
      samples/ControlCatalog/Pages/MenuPage.xaml
  4. 3 0
      samples/RenderDemo/MainWindow.xaml
  5. 92 0
      samples/RenderDemo/Pages/WriteableBitmapPage.cs
  6. 6 0
      src/Avalonia.Base/ApiCompatBaseline.txt
  7. 3 1
      src/Avalonia.Base/AvaloniaObject.cs
  8. 3 3
      src/Avalonia.Base/AvaloniaProperty.cs
  9. 8 17
      src/Avalonia.Base/DirectProperty.cs
  10. 22 17
      src/Avalonia.Base/DirectPropertyBase.cs
  11. 1 1
      src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs
  12. 7 10
      src/Avalonia.Controls/Repeater/ViewportManager.cs
  13. 7 3
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  14. 1 1
      src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs
  15. 2 0
      src/Avalonia.Themes.Fluent/ContextMenu.xaml
  16. 12 2
      src/Avalonia.Themes.Fluent/MenuItem.xaml
  17. 11 1
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  18. 20 1
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  19. 29 4
      src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs
  20. 21 0
      src/Avalonia.Visuals/Platform/AlphaFormat.cs
  21. 15 4
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  22. 3 6
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  23. 10 6
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  24. 22 0
      src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs
  25. 6 4
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  26. 8 9
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  27. 11 6
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  28. 2 2
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
  29. 6 3
      src/Windows/Avalonia.Direct2D1/PrimitiveExtensions.cs
  30. 23 0
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs
  31. 2 4
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  32. 74 31
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs
  33. 29 0
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs
  34. 1 2
      tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs
  35. 6 2
      tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
  36. 1 1
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  37. 15 0
      tests/Avalonia.Styling.UnitTests/SetterTests.cs
  38. 7 1
      tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
  39. 4 2
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

+ 1 - 1
samples/ControlCatalog/App.xaml

@@ -11,7 +11,7 @@
       <Setter Property="FontSize" Value="14" />
     </Style>
     <Style Selector="TextBlock.h3">
-      <Setter Property="FontSize" Value="10" />
+      <Setter Property="FontSize" Value="12" />
     </Style>
     <StyleInclude Source="/SideBar.xaml"/>
   </Application.Styles>

+ 3 - 2
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@@ -14,13 +14,14 @@
                     Padding="48,48,48,48">
                 <Border.ContextMenu>
                     <ContextMenu>
-                        <MenuItem Header="Standard _Menu Item"/>
+                        <MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A" />
+                        <MenuItem Header="_Disabled Menu Item" IsEnabled="False" InputGesture="Ctrl+D" />
                         <Separator/>
                         <MenuItem Header="Menu with _Submenu">
                             <MenuItem Header="Submenu _1"/>
                             <MenuItem Header="Submenu _2"/>
                         </MenuItem>
-                        <MenuItem Header="Menu Item with _Icon">
+                        <MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
                             <MenuItem.Icon>
                                 <Image Source="/Assets/github_icon.png"/>
                             </MenuItem.Icon>

+ 5 - 1
samples/ControlCatalog/Pages/MenuPage.xaml

@@ -16,13 +16,17 @@
                 <TextBlock Classes="h3" Margin="4 8">Defined in XAML</TextBlock>
                 <Menu>
                     <MenuItem Header="_First">
-                        <MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A"/>
+                        <MenuItem Header="Standard _Menu Item" InputGesture="Ctrl+A" />
+                        <MenuItem Header="_Disabled Menu Item" IsEnabled="False" InputGesture="Ctrl+D" />
                         <Separator/>
                         <MenuItem Header="Menu with _Submenu">
                             <MenuItem Header="Submenu _1"/>
                             <MenuItem Header="Submenu _2 with Submenu">
                                 <MenuItem Header="Submenu Level 2" />
                             </MenuItem>
+                            <MenuItem Header="Submenu _3 with Submenu Disabled" IsEnabled="False">
+                                <MenuItem Header="Submenu Level 2" />
+                            </MenuItem>                          
                         </MenuItem>
                         <MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
                             <MenuItem.Icon>

+ 3 - 0
samples/RenderDemo/MainWindow.xaml

@@ -44,6 +44,9 @@
       <TabItem Header="RenderTargetBitmap">
         <pages:RenderTargetBitmapPage/>
       </TabItem>
+      <TabItem Header="WriteableBitmap">
+        <pages:WriteableBitmapPage/>
+      </TabItem>
       <TabItem Header="GlyphRun">
         <pages:GlyphRunPage/>
       </TabItem>

+ 92 - 0
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);
+        }
+    }
+}

+ 6 - 0
src/Avalonia.Base/ApiCompatBaseline.txt

@@ -0,0 +1,6 @@
+Compat issues with assembly Avalonia.Base:
+MembersMustExist : Member 'public void Avalonia.DirectProperty<TOwner, TValue>..ctor(System.String, System.Func<TOwner, TValue>, System.Action<TOwner, TValue>, Avalonia.DirectPropertyMetadata<TValue>, System.Boolean)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase<TValue>..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<TValue>..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<TValue>.IsDataValidationEnabled.get()' does not exist in the implementation but it does exist in the contract.
+Total Issues: 4

+ 3 - 1
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);
             }

+ 3 - 3
src/Avalonia.Base/AvaloniaProperty.cs

@@ -369,14 +369,14 @@ namespace Avalonia
 
             var metadata = new DirectPropertyMetadata<TValue>(
                 unsetValue: unsetValue,
-                defaultBindingMode: defaultBindingMode);
+                defaultBindingMode: defaultBindingMode,
+                enableDataValidation: enableDataValidation);
 
             var result = new DirectProperty<TOwner, TValue>(
                 name,
                 getter,
                 setter,
-                metadata,
-                enableDataValidation);
+                metadata);
             AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);
             return result;
         }

+ 8 - 17
src/Avalonia.Base/DirectProperty.cs

@@ -23,16 +23,12 @@ namespace Avalonia
         /// <param name="getter">Gets the current value of the property.</param>
         /// <param name="setter">Sets the value of the property. May be null.</param>
         /// <param name="metadata">The property metadata.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
         public DirectProperty(
             string name,
             Func<TOwner, TValue> getter,
             Action<TOwner, TValue> setter,
-            DirectPropertyMetadata<TValue> metadata,
-            bool enableDataValidation)
-            : base(name, typeof(TOwner), metadata, enableDataValidation)
+            DirectPropertyMetadata<TValue> metadata)
+            : base(name, typeof(TOwner), metadata)
         {
             Contract.Requires<ArgumentNullException>(getter != null);
 
@@ -47,16 +43,12 @@ namespace Avalonia
         /// <param name="getter">Gets the current value of the property.</param>
         /// <param name="setter">Sets the value of the property. May be null.</param>
         /// <param name="metadata">Optional overridden metadata.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
         private DirectProperty(
             DirectPropertyBase<TValue> source,
             Func<TOwner, TValue> getter,
             Action<TOwner, TValue> setter,
-            DirectPropertyMetadata<TValue> metadata,
-            bool enableDataValidation)
-            : base(source, typeof(TOwner), metadata, enableDataValidation)
+            DirectPropertyMetadata<TValue> metadata)
+            : base(source, typeof(TOwner), metadata)
         {
             Contract.Requires<ArgumentNullException>(getter != null);
 
@@ -107,7 +99,8 @@ namespace Avalonia
         {
             var metadata = new DirectPropertyMetadata<TValue>(
                 unsetValue: unsetValue,
-                defaultBindingMode: defaultBindingMode);
+                defaultBindingMode: defaultBindingMode,
+                enableDataValidation: enableDataValidation);
 
             metadata.Merge(GetMetadata<TOwner>(), this);
 
@@ -115,8 +108,7 @@ namespace Avalonia
                 (DirectPropertyBase<TValue>)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;

+ 22 - 17
src/Avalonia.Base/DirectPropertyBase.cs

@@ -23,17 +23,12 @@ namespace Avalonia
         /// <param name="name">The name of the property.</param>
         /// <param name="ownerType">The type of the class that registers the property.</param>
         /// <param name="metadata">The property metadata.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
         protected DirectPropertyBase(
             string name,
             Type ownerType,
-            PropertyMetadata metadata,
-            bool enableDataValidation)
+            PropertyMetadata metadata)
             : base(name, ownerType, metadata)
         {
-            IsDataValidationEnabled = enableDataValidation;
         }
 
         /// <summary>
@@ -42,17 +37,12 @@ namespace Avalonia
         /// <param name="source">The property to copy.</param>
         /// <param name="ownerType">The new owner type.</param>
         /// <param name="metadata">Optional overridden metadata.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
         protected DirectPropertyBase(
             AvaloniaProperty source,
             Type ownerType,
-            PropertyMetadata metadata,
-            bool enableDataValidation)
+            PropertyMetadata metadata)
             : base(source, ownerType, metadata)
         {
-            IsDataValidationEnabled = enableDataValidation;
         }
 
         /// <summary>
@@ -60,11 +50,6 @@ namespace Avalonia
         /// </summary>
         public abstract Type Owner { get; }
 
-        /// <summary>
-        /// Gets a value that indicates whether data validation is enabled for the property.
-        /// </summary>
-        public bool IsDataValidationEnabled { get; }
-
         /// <summary>
         /// Gets the value of the property on the instance.
         /// </summary>
@@ -102,6 +87,26 @@ namespace Avalonia
             return (DirectPropertyMetadata<TValue>)base.GetMetadata(type);
         }
 
+        /// <summary>
+        /// Overrides the metadata for the property on the specified type.
+        /// </summary>
+        /// <typeparam name="T">The type.</typeparam>
+        /// <param name="metadata">The metadata.</param>
+        public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : IAvaloniaObject
+        {
+            base.OverrideMetadata(typeof(T), metadata);
+        }
+
+        /// <summary>
+        /// Overrides the metadata for the property on the specified type.
+        /// </summary>
+        /// <param name="type">The type.</param>
+        /// <param name="metadata">The metadata.</param>
+        public void OverrideMetadata(Type type, DirectPropertyMetadata<TValue> metadata)
+        {
+            base.OverrideMetadata(type, metadata);
+        }
+
         /// <inheritdoc/>
         public override void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data)
         {

+ 1 - 1
src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs

@@ -34,7 +34,7 @@ namespace Avalonia.Controls
         /// </summary>
         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;

+ 7 - 10
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;

+ 7 - 3
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@@ -21,7 +21,11 @@ namespace Avalonia.Headless
 
         public IEnumerable<string> 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<FormattedTextStyleSpan> 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));
         }        

+ 1 - 1
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<T>.FromUntyped(value);
+            _value = BindingValue<T>.FromUntyped(value);
 
             if (_isActive)
             {

+ 2 - 0
src/Avalonia.Themes.Fluent/ContextMenu.xaml

@@ -9,6 +9,8 @@
       <Border.ContextMenu>
         <ContextMenu>
           <MenuItem Header="Standard _Menu Item" />
+          <MenuItem Header="Disabled"
+                    IsEnabled="False" />
           <Separator />
           <MenuItem Header="Menu with _Submenu">
             <MenuItem Header="Submenu _1" />

+ 12 - 2
src/Avalonia.Themes.Fluent/MenuItem.xaml

@@ -8,6 +8,8 @@
             Height="200">
       <Menu VerticalAlignment="Top">
         <MenuItem Header="File">
+          <MenuItem Header="Disabled"
+                    IsEnabled="False" />
           <MenuItem Header="New"
                     InputGesture="Ctrl+N">
             <MenuItem Header="XML" />
@@ -83,7 +85,6 @@
                                 Content="{TemplateBinding Header}"
                                 VerticalAlignment="Center"
                                 HorizontalAlignment="Stretch"
-                                TextBlock.Foreground="{TemplateBinding Foreground}"
                                 Grid.Column="1">
                 <ContentPresenter.DataTemplates>
                   <DataTemplate DataType="sys:String">
@@ -213,6 +214,9 @@
   <Style Selector="MenuItem:selected /template/ Border#PART_LayoutRoot">
     <Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundPointerOver}" />
   </Style>
+  <Style Selector="MenuItem:selected /template/ ContentPresenter#PART_HeaderPresenter">
+    <Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPointerOver}" />
+  </Style>
   <Style Selector="MenuItem:selected /template/ TextBlock#PART_InputGestureText">
     <Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPointerOver}" />
   </Style>
@@ -224,6 +228,9 @@
   <Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover">
     <Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundPressed}" />
   </Style>
+  <Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover ContentPresenter#PART_HeaderPresenter">
+    <Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPressed}" />
+  </Style>
   <Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover TextBlock#PART_InputGestureText">
     <Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPressed}" />
   </Style>
@@ -231,9 +238,12 @@
     <Setter Property="Fill" Value="{DynamicResource MenuFlyoutSubItemChevronPressed}" />
   </Style>
 
-  <Style Selector="MenuItem:disabled">
+  <Style Selector="MenuItem:disabled /template/ Border#PART_LayoutRoot">
     <Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundDisabled}" />
   </Style>
+  <Style Selector="MenuItem:disabled /template/ ContentPresenter#PART_HeaderPresenter">
+    <Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundDisabled}" />
+  </Style>
   <Style Selector="MenuItem:disabled /template/ TextBlock#PART_InputGestureText">
     <Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundDisabled}" />
   </Style>

+ 11 - 1
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<Avalonia.Platform.PixelFormat>)' 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<Avalonia.Platform.PixelFormat>)' 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

+ 20 - 1
src/Avalonia.Visuals/Media/Imaging/Bitmap.cs

@@ -99,14 +99,33 @@ namespace Avalonia.Media.Imaging
         /// Initializes a new instance of the <see cref="Bitmap"/> class.
         /// </summary>
         /// <param name="format">The pixel format.</param>
+        /// <param name="alphaFormat">The alpha format.</param>
         /// <param name="data">The pointer to the source bytes.</param>
         /// <param name="size">The size of the bitmap in device pixels.</param>
         /// <param name="dpi">The DPI of the bitmap.</param>
         /// <param name="stride">The number of bytes per row.</param>
+        [Obsolete("Use overload taking an AlphaFormat.")]
         public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
+        {
+            var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
+
+            PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
+                .LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride));
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Bitmap"/> class.
+        /// </summary>
+        /// <param name="format">The pixel format.</param>
+        /// <param name="alphaFormat">The alpha format.</param>
+        /// <param name="data">The pointer to the source bytes.</param>
+        /// <param name="size">The size of the bitmap in device pixels.</param>
+        /// <param name="dpi">The DPI of the bitmap.</param>
+        /// <param name="stride">The number of bytes per row.</param>
+        public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
         {
             PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
-                .LoadBitmap(format, data, size, dpi, stride));
+                .LoadBitmap(format, alphaFormat, data, size, dpi, stride));
         }
 
         /// <inheritdoc/>

+ 29 - 4
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
         /// <param name="dpi">The DPI of the bitmap.</param>
         /// <param name="format">The pixel format (optional).</param>
         /// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
-        public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) 
-            : base(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().CreateWriteableBitmap(size, dpi, format))
+        [Obsolete("Use overload taking an AlphaFormat.")]
+        public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
+            : base(CreatePlatformImpl(size, dpi, format, null))
         {
         }
-        
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WriteableBitmap"/> class.
+        /// </summary>
+        /// <param name="size">The size of the bitmap in device pixels.</param>
+        /// <param name="dpi">The DPI of the bitmap.</param>
+        /// <param name="format">The pixel format (optional).</param>
+        /// <param name="alphaFormat">The alpha format (optional).</param>
+        /// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
+        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<IPlatformRenderInterface>();
+
+            PixelFormat finalFormat = format ?? ri.DefaultPixelFormat;
+            AlphaFormat finalAlphaFormat = alphaFormat ?? ri.DefaultAlphaFormat;
+
+            return ri.CreateWriteableBitmap(size, dpi, finalFormat, finalAlphaFormat);
+        }
     }
 }

+ 21 - 0
src/Avalonia.Visuals/Platform/AlphaFormat.cs

@@ -0,0 +1,21 @@
+namespace Avalonia.Platform
+{
+    /// <summary>
+    /// Describes how to interpret the alpha component of a pixel.
+    /// </summary>
+    public enum AlphaFormat
+    {
+        /// <summary>
+        /// All pixels have their alpha premultiplied in their color components.
+        /// </summary>
+        Premul,
+        /// <summary>
+        /// All pixels have their color components stored without any regard to the alpha. e.g. this is the default configuration for PNG images.
+        /// </summary>
+        Unpremul,
+        /// <summary>
+        /// All pixels are stored as opaque.
+        /// </summary>
+        Opaque
+    }
+}

+ 15 - 4
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
         /// </summary>
         /// <param name="size">The size of the bitmap in device pixels.</param>
         /// <param name="dpi">The DPI of the bitmap.</param>
-        /// <param name="format">Pixel format (optional).</param>
+        /// <param name="format">Pixel format.</param>
+        /// <param name="alphaFormat">Alpha format .</param>
         /// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
-        IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null);
+        IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat);
 
         /// <summary>
         /// Loads a bitmap implementation from a file..
@@ -124,12 +124,13 @@ namespace Avalonia.Platform
         /// Loads a bitmap implementation from a pixels in memory.
         /// </summary>
         /// <param name="format">The pixel format.</param>
+        /// <param name="alphaFormat">The alpha format.</param>
         /// <param name="data">The pointer to source bytes.</param>
         /// <param name="size">The size of the bitmap in device pixels.</param>
         /// <param name="dpi">The DPI of the bitmap.</param>
         /// <param name="stride">The number of bytes per row.</param>
         /// <returns>An <see cref="IBitmapImpl"/>.</returns>
-        IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride);
+        IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride);
 
         /// <summary>
         /// 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; }
+
+        /// <summary>
+        /// Default <see cref="AlphaFormat"/> used on this platform.
+        /// </summary>
+        public AlphaFormat DefaultAlphaFormat { get; }
+
+        /// <summary>
+        /// Default <see cref="PixelFormat"/> used on this platform.
+        /// </summary>
+        public PixelFormat DefaultPixelFormat { get; }
     }
 }

+ 3 - 6
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
         /// <param name="dpi">DPI of the bitmap.</param>
         /// <param name="stride">Stride of data pixels.</param>
         /// <param name="format">Format of data pixels.</param>
+        /// <param name="alphaFormat">Alpha format of data pixels.</param>
         /// <param name="data">Data pixels.</param>
-        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);
 

+ 10 - 6
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
         }
 
         /// <inheritdoc />
-        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);
         }
 
         /// <inheritdoc />
@@ -152,9 +152,9 @@ namespace Avalonia.Skia
         }
 
         /// <inheritdoc />
-        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; }
     }
 }

+ 22 - 0
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)

+ 6 - 4
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@@ -22,12 +22,14 @@ namespace Avalonia.Skia
         /// <param name="size">The size of the bitmap in device pixels.</param>
         /// <param name="dpi">The DPI of the bitmap.</param>
         /// <param name="format">The pixel format.</param>
-        public WriteableBitmapImpl(PixelSize size, Vector dpi, PixelFormat? format = null)
+        /// <param name="alphaFormat">The alpha format.</param>
+        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<IRuntimePlatform>();
             
@@ -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);

+ 8 - 9
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
         }
 
         /// <inheritdoc />
-        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;
     }
 }

+ 11 - 6
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
         /// <param name="size">The size of the bitmap in device pixels.</param>
         /// <param name="dpi">The DPI of the bitmap.</param>
         /// <param name="pixelFormat">Pixel format</param>
-        public WicBitmapImpl(PixelSize size, Vector dpi, APixelFormat? pixelFormat = null)
+        /// <param name="alphaFormat">Alpha format.</param>
+        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;
 

+ 2 - 2
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)
         {
         }
 

+ 6 - 3
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");
         }
 

+ 23 - 0
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<BindingValue<int>>();
+
+            // Class2 overrides `NonValidatedDirectProperty`'s metadata to enable data validation.
+            target.Bind(Class1.NonValidatedDirectProperty, source);
+            source.OnNext(1);
+
+            var result = target.Notifications.Cast<BindingValue<int>>().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<Class2>(
+                    new DirectPropertyMetadata<int>(enableDataValidation: true));
+            }
+        }
+
         public class ViewModel : NotifyingBase
         {
             private string _stringValue;

+ 2 - 4
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@@ -547,8 +547,7 @@ namespace Avalonia.Base.UnitTests
                 "foo",
                 o => "foo",
                 null,
-                new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay),
-                false);
+                new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay));
             var bar = foo.AddOwner<Class2>(o => "bar");
 
             Assert.Equal(BindingMode.TwoWay, bar.GetMetadata<Class1>().DefaultBindingMode);
@@ -562,8 +561,7 @@ namespace Avalonia.Base.UnitTests
                 "foo",
                 o => "foo",
                 null,
-                new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay),
-                false);
+                new DirectPropertyMetadata<string>(defaultBindingMode: BindingMode.TwoWay));
             var bar = foo.AddOwner<Class2>(o => "bar", defaultBindingMode: BindingMode.OneWayToSource);
 
             Assert.Equal(BindingMode.TwoWay, bar.GetMetadata<Class1>().DefaultBindingMode);

+ 74 - 31
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,57 +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);
         }
 
-        [Fact]
-        public void IsSet_Returns_False_For_Unset_Property()
+        public class StyledProperty : AvaloniaObjectTests_Metadata
         {
-            var target = new Class1();
+            [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.False(target.IsSet(Class1.FooProperty));
-        }
-
-        [Fact]
-        public void IsSet_Returns_False_For_Set_Property()
-        {
-            var target = new Class1();
+                Assert.Equal("foo", baseValue);
+                Assert.Equal("bar", derivedValue);
+            }
 
-            target.SetValue(Class1.FooProperty, "foo");
+            [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.True(target.IsSet(Class1.FooProperty));
+                Assert.Equal("foo", baseValue);
+                Assert.Equal("baz", addOwneredValue);
+            }
         }
 
-        [Fact]
-        public void IsSet_Returns_False_For_Cleared_Property()
+        public class DirectProperty : AvaloniaObjectTests_Metadata
         {
-            var target = new Class1();
+            [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));
 
-            target.SetValue(Class1.FooProperty, "foo");
-            target.SetValue(Class1.FooProperty, AvaloniaProperty.UnsetValue);
+                Assert.Equal("foo", baseValue);
+                Assert.Equal("bar", derivedValue);
+            }
 
-            Assert.False(target.IsSet(Class1.FooProperty));
+            [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<string> FooProperty =
-                AvaloniaProperty.Register<Class1, string>("Foo");
+            public static readonly StyledProperty<string> StyledProperty =
+                AvaloniaProperty.Register<Class1, string>("Styled", "foo");
+
+            public static readonly DirectProperty<Class1, string> DirectProperty =
+                AvaloniaProperty.RegisterDirect<Class1, string>("Styled", o => o.Direct, unsetValue: "foo");
+
+            private string _direct;
+
+            public string Direct
+            {
+                get => _direct;
+            }
         }
 
         private class Class2 : Class1
         {
-            public static readonly StyledProperty<string> BarProperty =
-                AvaloniaProperty.Register<Class2, string>("Bar");
+            static Class2()
+            {
+                StyledProperty.OverrideDefaultValue<Class2>("bar");
+                DirectProperty.OverrideMetadata<Class2>(new DirectPropertyMetadata<string>("bar"));
+            }
         }
 
-        private class AttachedOwner
+        private class Class3 : AvaloniaObject
         {
-            public static readonly AttachedProperty<string> AttachedProperty =
-                AvaloniaProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached");
+            public static readonly StyledProperty<string> StyledProperty =
+                Class1.StyledProperty.AddOwner<Class3>();
+
+            public static readonly DirectProperty<Class3, string> DirectProperty =
+                Class1.DirectProperty.AddOwner<Class3>(o => o.Direct, unsetValue: "baz");
+
+            private string _direct;
+
+            static Class3()
+            {
+                StyledProperty.OverrideDefaultValue<Class3>("baz");
+            }
+
+            public string Direct
+            {
+                get => _direct;
+            }
         }
     }
 }

+ 29 - 0
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()
         {

+ 1 - 2
tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs

@@ -11,8 +11,7 @@ namespace Avalonia.Base.UnitTests
                 "test", 
                 o => null, 
                 null,
-                new DirectPropertyMetadata<string>(),
-                false);
+                new DirectPropertyMetadata<string>());
 
             Assert.True(target.IsDirect);
         }

+ 6 - 2
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;
     }
 }

+ 1 - 1
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)))
             {

+ 15 - 0
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<object>(AvaloniaProperty.UnsetValue);
+            var descriptor = InstancedBinding.OneWay(subject);
+            var binding = Mock.Of<IBinding>(x => x.Initiate(control, TextBlock.TagProperty, null, false) == descriptor);
+            var style = Mock.Of<IStyle>();
+            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()
         {

+ 7 - 1
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,
+            AlphaFormat alphaFormat)
         {
             throw new NotImplementedException();
         }
@@ -87,6 +88,7 @@ namespace Avalonia.UnitTests
 
         public IBitmapImpl LoadBitmap(
             PixelFormat format,
+            AlphaFormat alphaFormat,
             IntPtr data,
             PixelSize size,
             Vector dpi,
@@ -102,5 +104,9 @@ namespace Avalonia.UnitTests
         }
 
         public bool SupportsIndividualRoundRects { get; set; }
+
+        public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
+
+        public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888;
     }
 }

+ 4 - 2
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();
         }
@@ -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)
+        public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat fmt, AlphaFormat alphaFormat)
         {
             throw new NotImplementedException();
         }