فهرست منبع

Merge pull request #3378 from AvaloniaUI/feature/drawing-image

Display Drawings in Image control
Steven Kirk 5 سال پیش
والد
کامیت
3d3ef68d4b
30فایلهای تغییر یافته به همراه270 افزوده شده و 120 حذف شده
  1. 5 1
      samples/ControlCatalog/MainView.xaml
  2. 46 39
      samples/ControlCatalog/Pages/ImagePage.xaml
  3. 20 19
      samples/ControlCatalog/Pages/ImagePage.xaml.cs
  4. 1 1
      samples/RenderDemo/Pages/RenderTargetBitmapPage.cs
  5. 3 1
      src/Avalonia.Controls/DrawingPresenter.cs
  6. 8 9
      src/Avalonia.Controls/Image.cs
  7. 1 1
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  8. 4 2
      src/Avalonia.Visuals/Media/Drawing.cs
  9. 16 5
      src/Avalonia.Visuals/Media/DrawingContext.cs
  10. 2 1
      src/Avalonia.Visuals/Media/DrawingGroup.cs
  11. 81 0
      src/Avalonia.Visuals/Media/DrawingImage.cs
  12. 4 1
      src/Avalonia.Visuals/Media/GeometryDrawing.cs
  13. 29 0
      src/Avalonia.Visuals/Media/IImage.cs
  14. 20 0
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  15. 1 10
      src/Avalonia.Visuals/Media/Imaging/IBitmap.cs
  16. 2 2
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  17. 3 3
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  18. 2 2
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  19. 1 1
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  20. 1 1
      src/Avalonia.X11/X11IconLoader.cs
  21. 1 1
      src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  22. 4 4
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  23. 2 2
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  24. 1 1
      src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
  25. 2 1
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs
  26. 1 1
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
  27. 6 8
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  28. 1 1
      tests/Avalonia.Controls.UnitTests/ImageTests.cs
  29. 1 1
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  30. 1 1
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

+ 5 - 1
samples/ControlCatalog/MainView.xaml

@@ -32,7 +32,11 @@
       <TabItem Header="DatePicker"><pages:DatePickerPage/></TabItem>
       <TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem>
       <TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
-      <TabItem Header="Image"><pages:ImagePage/></TabItem>
+      <TabItem Header="Image"
+               ScrollViewer.VerticalScrollBarVisibility="Disabled"
+               ScrollViewer.HorizontalScrollBarVisibility="Disabled">
+        <pages:ImagePage/>
+      </TabItem>
       <TabItem Header="ItemsRepeater"
                ScrollViewer.VerticalScrollBarVisibility="Disabled"
                ScrollViewer.HorizontalScrollBarVisibility="Disabled">

+ 46 - 39
samples/ControlCatalog/Pages/ImagePage.xaml

@@ -1,45 +1,52 @@
 <UserControl xmlns="https://github.com/avaloniaui"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              x:Class="ControlCatalog.Pages.ImagePage">
-  <StackPanel Orientation="Vertical" Spacing="4">
-    <TextBlock Classes="h1">Image</TextBlock>
-    <TextBlock Classes="h2">Displays an image</TextBlock>
-
-    <StackPanel Orientation="Horizontal"
-                Margin="0,16,0,0"
-                HorizontalAlignment="Center"
-                Spacing="16">
-      <StackPanel Orientation="Vertical">
-        <TextBlock>No Stretch</TextBlock>
-        <Image Source="/Assets/delicate-arch-896885_640.jpg"
-               Width="100" Height="200"
-               Stretch="None"/>
-      </StackPanel>
-
-      <StackPanel Orientation="Vertical">
-        <TextBlock>Fill</TextBlock>
-        <Image Source="/Assets/delicate-arch-896885_640.jpg"
-               Width="100" Height="200"
-               Stretch="Fill"/>
-      </StackPanel>
+  <DockPanel>
+    <StackPanel DockPanel.Dock="Top" Orientation="Vertical" Spacing="4">
+      <TextBlock Classes="h1">Image</TextBlock>
+      <TextBlock Classes="h2">Displays an image</TextBlock>
+    </StackPanel>
 
-      <StackPanel Orientation="Vertical">
-        <TextBlock>Uniform</TextBlock>
-        <Image Source="/Assets/delicate-arch-896885_640.jpg"
-                Width="100" Height="200"
-                Stretch="Uniform"/>
-      </StackPanel>
+    <Grid ColumnDefinitions="*,*" RowDefinitions="Auto,*" Margin="64">
+      
+      <DockPanel Grid.Column="0" Grid.Row="1" Margin="16">
+        <TextBlock DockPanel.Dock="Top" Classes="h3" Margin="0 8">Bitmap</TextBlock>
+        <ComboBox Name="bitmapStretch" DockPanel.Dock="Top" SelectedIndex="2" SelectionChanged="BitmapStretchChanged">
+          <ComboBoxItem>None</ComboBoxItem>
+          <ComboBoxItem>Fill</ComboBoxItem>
+          <ComboBoxItem>Uniform</ComboBoxItem>
+          <ComboBoxItem>UniformToFill</ComboBoxItem>
+        </ComboBox>
+        <Image Name="bitmapImage"
+               Source="/Assets/delicate-arch-896885_640.jpg"/>
+      </DockPanel>
 
-      <StackPanel Orientation="Vertical">
-        <TextBlock>UniformToFill</TextBlock>
-        <Image Source="/Assets/delicate-arch-896885_640.jpg"
-               Width="100" Height="200"
-               Stretch="UniformToFill"/>
-      </StackPanel>
-    </StackPanel>
-    <StackPanel Orientation="Vertical">
-      <TextBlock>Window Icon as an Image</TextBlock>
-      <Image Name="Icon" Width="100" Height="200" Stretch="None" />
-    </StackPanel>
-  </StackPanel>
+      <DockPanel Grid.Column="1" Grid.Row="1" Margin="16">
+        <TextBlock DockPanel.Dock="Top" Classes="h3" Margin="0 8">Drawing</TextBlock>
+        <ComboBox Name="drawingStretch" DockPanel.Dock="Top" SelectedIndex="2" SelectionChanged="DrawingStretchChanged">
+          <ComboBoxItem>None</ComboBoxItem>
+          <ComboBoxItem>Fill</ComboBoxItem>
+          <ComboBoxItem>Uniform</ComboBoxItem>
+          <ComboBoxItem>UniformToFill</ComboBoxItem>
+        </ComboBox>
+        <Image Name="drawingImage">
+          <Image.Source>
+            <DrawingImage>
+              <GeometryDrawing Brush="Red">
+                <PathGeometry>
+                  <PathFigure StartPoint="0,0" IsClosed="True">
+                    <QuadraticBezierSegment Point1="50,0" Point2="50,-50" />
+                    <QuadraticBezierSegment Point1="100,-50" Point2="100,0" />
+                    <LineSegment Point="50,0" />
+                    <LineSegment Point="50,50" />
+                  </PathFigure>
+                </PathGeometry>
+              </GeometryDrawing>
+            </DrawingImage>
+          </Image.Source>
+        </Image>
+      </DockPanel>
+    </Grid>
+    
+  </DockPanel>
 </UserControl>

+ 20 - 19
samples/ControlCatalog/Pages/ImagePage.xaml.cs

@@ -1,40 +1,41 @@
-using System.IO;
-using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
-using Avalonia.Media.Imaging;
+using Avalonia.Media;
 
 namespace ControlCatalog.Pages
 {
     public class ImagePage : UserControl
     {
-        private Image iconImage;
+        private readonly Image _bitmapImage;
+        private readonly Image _drawingImage;
+
         public ImagePage()
         {
-            this.InitializeComponent();
+            InitializeComponent();
+            _bitmapImage = this.FindControl<Image>("bitmapImage");
+            _drawingImage = this.FindControl<Image>("drawingImage");
         }
 
         private void InitializeComponent()
         {
             AvaloniaXamlLoader.Load(this);
-            iconImage = this.Get<Image>("Icon");
         }
 
-        protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+        public void BitmapStretchChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (_bitmapImage != null)
+            {
+                var comboxBox = (ComboBox)sender;
+                _bitmapImage.Stretch = (Stretch)comboxBox.SelectedIndex;
+            }
+        }
+
+        public void DrawingStretchChanged(object sender, SelectionChangedEventArgs e)
         {
-            base.OnAttachedToVisualTree(e);
-            if (iconImage.Source == null)
+            if (_drawingImage != null)
             {
-                var windowRoot = e.Root as Window;
-                if (windowRoot != null)
-                {
-                    using (var stream = new MemoryStream())
-                    {
-                        windowRoot.Icon.Save(stream);
-                        stream.Seek(0, SeekOrigin.Begin);
-                        iconImage.Source = new Bitmap(stream);
-                    }
-                }
+                var comboxBox = (ComboBox)sender;
+                _drawingImage.Stretch = (Stretch)comboxBox.SelectedIndex;
             }
         }
     }

+ 1 - 1
samples/RenderDemo/Pages/RenderTargetBitmapPage.cs

@@ -39,7 +39,7 @@ namespace RenderDemo.Pages
                 ctx.FillRectangle(Brushes.Fuchsia, new Rect(50, 50, 100, 100));
             }
 
-            context.DrawImage(_bitmap, 1, 
+            context.DrawImage(_bitmap,
                 new Rect(0, 0, 200, 200), 
                 new Rect(0, 0, 200, 200));
             Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);

+ 3 - 1
src/Avalonia.Controls/DrawingPresenter.cs

@@ -1,9 +1,11 @@
-using Avalonia.Controls.Shapes;
+using System;
+using Avalonia.Controls.Shapes;
 using Avalonia.Media;
 using Avalonia.Metadata;
 
 namespace Avalonia.Controls
 {
+    [Obsolete("Use Image control with DrawingImage source")]
     public class DrawingPresenter : Control
     {
         static DrawingPresenter()

+ 8 - 9
src/Avalonia.Controls/Image.cs

@@ -14,8 +14,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// Defines the <see cref="Source"/> property.
         /// </summary>
-        public static readonly StyledProperty<IBitmap> SourceProperty =
-            AvaloniaProperty.Register<Image, IBitmap>(nameof(Source));
+        public static readonly StyledProperty<IImage> SourceProperty =
+            AvaloniaProperty.Register<Image, IImage>(nameof(Source));
 
         /// <summary>
         /// Defines the <see cref="Stretch"/> property.
@@ -38,9 +38,9 @@ namespace Avalonia.Controls
         }
 
         /// <summary>
-        /// Gets or sets the bitmap image that will be displayed.
+        /// Gets or sets the image that will be displayed.
         /// </summary>
-        public IBitmap Source
+        public IImage Source
         {
             get { return GetValue(SourceProperty); }
             set { SetValue(SourceProperty, value); }
@@ -75,7 +75,7 @@ namespace Avalonia.Controls
             if (source != null)
             {
                 Rect viewPort = new Rect(Bounds.Size);
-                Size sourceSize = new Size(source.PixelSize.Width, source.PixelSize.Height);
+                Size sourceSize = source.Size;
                 Vector scale = Stretch.CalculateScaling(Bounds.Size, sourceSize, StretchDirection);
                 Size scaledSize = sourceSize * scale;
                 Rect destRect = viewPort
@@ -86,7 +86,7 @@ namespace Avalonia.Controls
 
                 var interpolationMode = RenderOptions.GetBitmapInterpolationMode(this);
 
-                context.DrawImage(source, 1, sourceRect, destRect, interpolationMode);
+                context.DrawImage(source, sourceRect, destRect, interpolationMode);
             }
         }
 
@@ -102,8 +102,7 @@ namespace Avalonia.Controls
 
             if (source != null)
             {
-                var sourceSize = new Size(source.PixelSize.Width, source.PixelSize.Height);
-                result = Stretch.CalculateSize(availableSize, sourceSize, StretchDirection);
+                result = Stretch.CalculateSize(availableSize, source.Size, StretchDirection);
             }
 
             return result;
@@ -116,7 +115,7 @@ namespace Avalonia.Controls
 
             if (source != null)
             {
-                var sourceSize = new Size(source.PixelSize.Width, source.PixelSize.Height);
+                var sourceSize = source.Size;
                 var result = Stretch.CalculateSize(finalSize, sourceSize);
                 return result;
             }

+ 1 - 1
src/Avalonia.Controls/Remote/RemoteWidget.cs

@@ -83,7 +83,7 @@ namespace Avalonia.Controls.Remote
                         Marshal.Copy(_lastFrame.Data, y * _lastFrame.Stride,
                             new IntPtr(l.Address.ToInt64() + l.RowBytes * y), lineLen);
                 }
-                context.DrawImage(_bitmap, 1, new Rect(0, 0, _bitmap.PixelSize.Width, _bitmap.PixelSize.Height),
+                context.DrawImage(_bitmap, new Rect(0, 0, _bitmap.PixelSize.Width, _bitmap.PixelSize.Height),
                     new Rect(Bounds.Size));
             }
             base.Render(context);

+ 4 - 2
src/Avalonia.Visuals/Media/Drawing.cs

@@ -1,4 +1,6 @@
-namespace Avalonia.Media
+using Avalonia.Platform;
+
+namespace Avalonia.Media
 {
     public abstract class Drawing : AvaloniaObject
     {
@@ -6,4 +8,4 @@
 
         public abstract Rect GetBounds();
     }
-}
+}

+ 16 - 5
src/Avalonia.Visuals/Media/DrawingContext.cs

@@ -74,18 +74,29 @@ namespace Avalonia.Media
         public Matrix CurrentContainerTransform => _currentContainerTransform;
 
         /// <summary>
-        /// Draws a bitmap image.
+        /// Draws an image.
         /// </summary>
-        /// <param name="source">The bitmap image.</param>
-        /// <param name="opacity">The opacity to draw with.</param>
+        /// <param name="source">The image.</param>
+        /// <param name="rect">The rect in the output to draw to.</param>
+        public void DrawImage(IImage source, Rect rect)
+        {
+            Contract.Requires<ArgumentNullException>(source != null);
+
+            DrawImage(source, new Rect(source.Size), rect);
+        }
+
+        /// <summary>
+        /// Draws an image.
+        /// </summary>
+        /// <param name="source">The image.</param>
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
         /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
-        public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
+        public void DrawImage(IImage source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
         {
             Contract.Requires<ArgumentNullException>(source != null);
 
-            PlatformImpl.DrawImage(source.PlatformImpl, opacity, sourceRect, destRect, bitmapInterpolationMode);
+            source.Draw(this, sourceRect, destRect, bitmapInterpolationMode);
         }
 
         /// <summary>

+ 2 - 1
src/Avalonia.Visuals/Media/DrawingGroup.cs

@@ -1,5 +1,6 @@
 using Avalonia.Collections;
 using Avalonia.Metadata;
+using Avalonia.Platform;
 
 namespace Avalonia.Media
 {
@@ -55,4 +56,4 @@ namespace Avalonia.Media
             return rect;
         }
     }
-}
+}

+ 81 - 0
src/Avalonia.Visuals/Media/DrawingImage.cs

@@ -0,0 +1,81 @@
+using System;
+using Avalonia.Metadata;
+using Avalonia.Platform;
+using Avalonia.Visuals.Media.Imaging;
+
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// An <see cref="IImage"/> that uses a <see cref="Drawing"/> for content.
+    /// </summary>
+    public class DrawingImage : AvaloniaObject, IImage, IAffectsRender
+    {
+        /// <summary>
+        /// Defines the <see cref="Drawing"/> property.
+        /// </summary>
+        public static readonly StyledProperty<Drawing> DrawingProperty =
+            AvaloniaProperty.Register<DrawingImage, Drawing>(nameof(Drawing));
+
+        /// <inheritdoc/>
+        public event EventHandler Invalidated;
+
+        /// <summary>
+        /// Gets or sets the drawing content.
+        /// </summary>
+        [Content]
+        public Drawing Drawing
+        {
+            get => GetValue(DrawingProperty);
+            set => SetValue(DrawingProperty, value);
+        }
+
+        /// <inheritdoc/>
+        public Size Size => Drawing?.GetBounds().Size ?? default;
+
+        /// <inheritdoc/>
+        void IImage.Draw(
+            DrawingContext context,
+            Rect sourceRect,
+            Rect destRect,
+            BitmapInterpolationMode bitmapInterpolationMode)
+        {
+            var drawing = Drawing;
+
+            if (drawing == null)
+            {
+                return;
+            }
+
+            var bounds = drawing.GetBounds();
+            var scale = Matrix.CreateScale(
+                destRect.Width / sourceRect.Width,
+                destRect.Height / sourceRect.Height);
+            var translate = Matrix.CreateTranslation(
+                -sourceRect.X + destRect.X - bounds.X,
+                -sourceRect.Y + destRect.Y - bounds.Y);
+
+            using (context.PushClip(destRect))
+            using (context.PushPreTransform(translate * scale))
+            {
+                Drawing?.Draw(context);
+            }
+        }
+
+        /// <inheritdoc/>
+        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            base.OnPropertyChanged(e);
+
+            if (e.Property == DrawingProperty)
+            {
+                RaiseInvalidated(EventArgs.Empty);
+            }
+        }
+
+        /// <summary>
+        /// Raises the <see cref="Invalidated"/> event.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e);
+    }
+}

+ 4 - 1
src/Avalonia.Visuals/Media/GeometryDrawing.cs

@@ -1,10 +1,13 @@
-namespace Avalonia.Media
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
 {
     public class GeometryDrawing : Drawing
     {
         public static readonly StyledProperty<Geometry> GeometryProperty =
             AvaloniaProperty.Register<GeometryDrawing, Geometry>(nameof(Geometry));
 
+        [Content]
         public Geometry Geometry
         {
             get => GetValue(GeometryProperty);

+ 29 - 0
src/Avalonia.Visuals/Media/IImage.cs

@@ -0,0 +1,29 @@
+using Avalonia.Platform;
+using Avalonia.Visuals.Media.Imaging;
+
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// Represents a raster or vector image.
+    /// </summary>
+    public interface IImage
+    {
+        /// <summary>
+        /// Gets the size of the image, in device independent pixels.
+        /// </summary>
+        Size Size { get; }
+
+        /// <summary>
+        /// Draws the image to a <see cref="DrawingContext"/>.
+        /// </summary>
+        /// <param name="context">The drawing context.</param>
+        /// <param name="sourceRect">The rect in the image to draw.</param>
+        /// <param name="destRect">The rect in the output to draw to.</param>
+        /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
+        void Draw(
+            DrawingContext context,
+            Rect sourceRect,
+            Rect destRect,
+            BitmapInterpolationMode bitmapInterpolationMode);
+    }
+}

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

@@ -5,6 +5,7 @@ using System;
 using System.IO;
 using Avalonia.Platform;
 using Avalonia.Utilities;
+using Avalonia.Visuals.Media.Imaging;
 
 namespace Avalonia.Media.Imaging
 {
@@ -94,9 +95,28 @@ namespace Avalonia.Media.Imaging
             PlatformImpl.Item.Save(fileName);
         }
 
+        /// <summary>
+        /// Saves the bitmap to a stream.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
         public void Save(Stream stream)
         {
             PlatformImpl.Item.Save(stream);
         }
+
+        /// <inheritdoc/>
+        void IImage.Draw(
+            DrawingContext context,
+            Rect sourceRect,
+            Rect destRect,
+            BitmapInterpolationMode bitmapInterpolationMode)
+        {
+            context.PlatformImpl.DrawBitmap(
+                PlatformImpl,
+                1,
+                sourceRect,
+                destRect,
+                bitmapInterpolationMode);
+        }
     }
 }

+ 1 - 10
src/Avalonia.Visuals/Media/Imaging/IBitmap.cs

@@ -11,7 +11,7 @@ namespace Avalonia.Media.Imaging
     /// <summary>
     /// Represents a bitmap image.
     /// </summary>
-    public interface IBitmap : IDisposable
+    public interface IBitmap : IImage, IDisposable
     {
         /// <summary>
         /// Gets the dots per inch (DPI) of the image.
@@ -32,15 +32,6 @@ namespace Avalonia.Media.Imaging
         /// </summary>
         IRef<IBitmapImpl> PlatformImpl { get; }
 
-        /// <summary>
-        /// Gets the size of the image, in device independent pixels.
-        /// </summary>
-        /// <remarks>
-        /// Note that Skia does not currently support reading the DPI of an image so this value
-        /// will equal <see cref="PixelSize"/> on Skia.
-        /// </remarks>
-        Size Size { get; }
-
         /// <summary>
         /// Saves the bitmap to a file.
         /// </summary>

+ 2 - 2
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@@ -33,7 +33,7 @@ namespace Avalonia.Platform
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
         /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
-        void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default);
+        void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default);
 
         /// <summary>
         /// Draws a bitmap image.
@@ -42,7 +42,7 @@ namespace Avalonia.Platform
         /// <param name="opacityMask">The opacity mask to draw with.</param>
         /// <param name="opacityMaskRect">The destination rect for the opacity mask.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect);
+        void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect);
 
         /// <summary>
         /// Draws a line.

+ 3 - 3
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -469,11 +469,11 @@ namespace Avalonia.Rendering
 
                 if (layer.OpacityMask == null)
                 {
-                    context.DrawImage(bitmap, layer.Opacity, sourceRect, clientRect);
+                    context.DrawBitmap(bitmap, layer.Opacity, sourceRect, clientRect);
                 }
                 else
                 {
-                    context.DrawImage(bitmap, layer.OpacityMask, layer.OpacityMaskRect, sourceRect);
+                    context.DrawBitmap(bitmap, layer.OpacityMask, layer.OpacityMaskRect, sourceRect);
                 }
 
                 if (layer.GeometryClip != null)
@@ -485,7 +485,7 @@ namespace Avalonia.Rendering
             if (_overlay != null)
             {
                 var sourceRect = new Rect(0, 0, _overlay.Item.PixelSize.Width, _overlay.Item.PixelSize.Height);
-                context.DrawImage(_overlay, 0.5, sourceRect, clientRect);
+                context.DrawBitmap(_overlay, 0.5, sourceRect, clientRect);
             }
 
             if (DrawFps)

+ 2 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@@ -115,7 +115,7 @@ namespace Avalonia.Rendering.SceneGraph
         }
 
         /// <inheritdoc/>
-        public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
+        public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
         {
             var next = NextDrawAs<ImageNode>();
 
@@ -130,7 +130,7 @@ namespace Avalonia.Rendering.SceneGraph
         }
 
         /// <inheritdoc/>
-        public void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect sourceRect)
+        public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect sourceRect)
         {
             // This method is currently only used to composite layers so shouldn't be called here.
             throw new NotSupportedException();

+ 1 - 1
src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs

@@ -100,7 +100,7 @@ namespace Avalonia.Rendering.SceneGraph
         public override void Render(IDrawingContextImpl context)
         {
             context.Transform = Transform;
-            context.DrawImage(Source, Opacity, SourceRect, DestRect, BitmapInterpolationMode);
+            context.DrawBitmap(Source, Opacity, SourceRect, DestRect, BitmapInterpolationMode);
         }
 
         /// <inheritdoc/>

+ 1 - 1
src/Avalonia.X11/X11IconLoader.cs

@@ -59,7 +59,7 @@ namespace Avalonia.X11
             }
             using(var rt = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().CreateRenderTarget(new[]{this}))
             using (var ctx = rt.CreateDrawingContext(null))
-                ctx.DrawImage(bitmap.PlatformImpl, 1, new Rect(bitmap.Size),
+                ctx.DrawBitmap(bitmap.PlatformImpl, 1, new Rect(bitmap.Size),
                     new Rect(0, 0, _width, _height));
             Data = new UIntPtr[_width * _height + 2];
             Data[0] = new UIntPtr((uint)_width);

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@@ -99,7 +99,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                 void Add(string type, string conv)
                     => AddType(typeSystem.GetType(type), typeSystem.GetType(conv));
                 
-                Add("Avalonia.Media.Imaging.IBitmap","Avalonia.Markup.Xaml.Converters.BitmapTypeConverter");
+                Add("Avalonia.Media.IImage","Avalonia.Markup.Xaml.Converters.BitmapTypeConverter");
                 var ilist = typeSystem.GetType("System.Collections.Generic.IList`1");
                 AddType(ilist.MakeGenericType(typeSystem.GetType("Avalonia.Point")),
                     typeSystem.GetType("Avalonia.Markup.Xaml.Converters.PointsListTypeConverter"));

+ 4 - 4
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -110,7 +110,7 @@ namespace Avalonia.Skia
         }
 
         /// <inheritdoc />
-        public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
+        public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
         {
             var drawableImage = (IDrawableBitmapImpl)source.Item;
             var s = sourceRect.ToSKRect();
@@ -146,10 +146,10 @@ namespace Avalonia.Skia
         }
 
         /// <inheritdoc />
-        public void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
+        public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
         {
             PushOpacityMask(opacityMask, opacityMaskRect);
-            DrawImage(source, 1, new Rect(0, 0, source.Item.PixelSize.Width, source.Item.PixelSize.Height), destRect, BitmapInterpolationMode.Default);
+            DrawBitmap(source, 1, new Rect(0, 0, source.Item.PixelSize.Width, source.Item.PixelSize.Height), destRect, BitmapInterpolationMode.Default);
             PopOpacityMask();
         }
 
@@ -437,7 +437,7 @@ namespace Avalonia.Skia
                 context.Clear(Colors.Transparent);
                 context.PushClip(calc.IntermediateClip);
                 context.Transform = calc.IntermediateTransform;
-                context.DrawImage(
+                context.DrawBitmap(
                     RefCountable.CreateUnownedNotClonable(tileBrushImage),
                     1,
                     sourceRect,

+ 2 - 2
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@@ -109,7 +109,7 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
         /// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
-        public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
+        public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
         {
             using (var d2d = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext))
             {
@@ -149,7 +149,7 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="opacityMask">The opacity mask to draw with.</param>
         /// <param name="opacityMaskRect">The destination rect for the opacity mask.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        public void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
+        public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
         {
             using (var d2dSource = ((BitmapImpl)source.Item).GetDirect2DBitmap(_deviceContext))
             using (var sourceBrush = new BitmapBrush(_deviceContext, d2dSource.Value))

+ 1 - 1
src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs

@@ -107,7 +107,7 @@ namespace Avalonia.Direct2D1.Media
                 context.PushClip(calc.IntermediateClip);
                 context.Transform = calc.IntermediateTransform;
                 
-                context.DrawImage(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect, _bitmapInterpolationMode);
+                context.DrawBitmap(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect, _bitmapInterpolationMode);
                 context.PopClip();
             }
 

+ 2 - 1
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs

@@ -30,7 +30,7 @@ namespace Avalonia.Direct2D1.Media
             _direct2DBitmap = d2DBitmap ?? throw new ArgumentNullException(nameof(d2DBitmap));
         }
 
-        public override Vector Dpi => _direct2DBitmap.DotsPerInch.ToAvaloniaVector();
+        public override Vector Dpi => new Vector(96, 96);
         public override PixelSize PixelSize => _direct2DBitmap.PixelSize.ToAvalonia();
 
         public override void Dispose()
@@ -58,3 +58,4 @@ namespace Avalonia.Direct2D1.Media
         }
     }
 }
+;

+ 1 - 1
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs

@@ -58,7 +58,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
             {
                 using (var dc = wic.CreateDrawingContext(null))
                 {
-                    dc.DrawImage(
+                    dc.DrawBitmap(
                         RefCountable.CreateUnownedNotClonable(this),
                         1,
                         new Rect(PixelSize.ToSizeWithDpi(Dpi.X)),

+ 6 - 8
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@@ -26,6 +26,7 @@ namespace Avalonia.Direct2D1.Media
             using (BitmapDecoder decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, fileName, DecodeOptions.CacheOnDemand))
             {
                 WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnDemand);
+                Dpi = new Vector(96, 96);
             }
         }
 
@@ -39,6 +40,7 @@ namespace Avalonia.Direct2D1.Media
             _decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, stream, DecodeOptions.CacheOnLoad);
 
             WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, _decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnLoad);
+            Dpi = new Vector(96, 96);
         }
 
         /// <summary>
@@ -62,6 +64,7 @@ namespace Avalonia.Direct2D1.Media
                 pixelFormat.Value.ToWic(),
                 BitmapCreateCacheOption.CacheOnLoad);
             WicImpl.SetResolution(dpi.X, dpi.Y);
+            Dpi = dpi;
         }
 
         public WicBitmapImpl(APixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
@@ -70,6 +73,8 @@ namespace Avalonia.Direct2D1.Media
             WicImpl.SetResolution(dpi.X, dpi.Y);
 
             PixelFormat = format;
+            Dpi = dpi;
+
             using (var l = WicImpl.Lock(BitmapLockFlags.Write))
             {
                 for (var row = 0; row < size.Height; row++)
@@ -82,14 +87,7 @@ namespace Avalonia.Direct2D1.Media
             }
         }
 
-        public override Vector Dpi
-        {
-            get
-            {
-                WicImpl.GetResolution(out double x, out double y);
-                return new Vector(x, y);
-            }
-        }
+        public override Vector Dpi { get; }
 
         public override PixelSize PixelSize => WicImpl.Size.ToAvalonia();
 

+ 1 - 1
tests/Avalonia.Controls.UnitTests/ImageTests.cs

@@ -169,7 +169,7 @@ namespace Avalonia.Controls.UnitTests
 
         private IBitmap CreateBitmap(int width, int height)
         {
-            return Mock.Of<IBitmap>(x => x.PixelSize == new PixelSize(width, height));
+            return Mock.Of<IBitmap>(x => x.Size == new Size(width, height));
         }
     }
 }

+ 1 - 1
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@@ -94,7 +94,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
                     ctx.DrawRectangle(Brushes.Pink, null, new Rect(0, 20, 100, 10));
 
                     var rc = new Rect(0, 0, 60, 60);
-                    ctx.DrawImage(bmp.PlatformImpl, 1, rc, rc);
+                    ctx.DrawBitmap(bmp.PlatformImpl, 1, rc, rc);
                 }
                 rtb.Save(System.IO.Path.Combine(OutputPath, testName + ".out.png"));
             }

+ 1 - 1
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@@ -670,7 +670,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
             var context = Mock.Get(target.RenderTarget.CreateDrawingContext(null));
             var borderLayer = target.Layers[border].Bitmap;
 
-            context.Verify(x => x.DrawImage(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>(), BitmapInterpolationMode.Default));
+            context.Verify(x => x.DrawBitmap(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>(), BitmapInterpolationMode.Default));
         }
 
         [Fact]