Browse Source

Merge branch 'master' into avalonia-object-thread-affinity

Steven Kirk 8 years ago
parent
commit
708ee7c57e
35 changed files with 330 additions and 64 deletions
  1. 2 2
      src/Avalonia.Controls/Image.cs
  2. 1 1
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  3. 1 1
      src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs
  4. 7 1
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  5. 24 4
      src/Avalonia.Visuals/Media/GradientBrush.cs
  6. 21 0
      src/Avalonia.Visuals/Media/IGradientBrush.cs
  7. 18 0
      src/Avalonia.Visuals/Media/ILinearGradientBrush.cs
  8. 24 0
      src/Avalonia.Visuals/Media/IRadialGradientBrush.cs
  9. 39 0
      src/Avalonia.Visuals/Media/ITileBrush.cs
  10. 15 0
      src/Avalonia.Visuals/Media/IVisualBrush.cs
  11. 1 1
      src/Avalonia.Visuals/Media/ImageBrush.cs
  12. 15 0
      src/Avalonia.Visuals/Media/Imaging/IImageBrush.cs
  13. 1 1
      src/Avalonia.Visuals/Media/LinearGradientBrush.cs
  14. 5 3
      src/Avalonia.Visuals/Media/RadialGradientBrush.cs
  15. 1 1
      src/Avalonia.Visuals/Media/TileBrush.cs
  16. 1 1
      src/Avalonia.Visuals/Media/VisualBrush.cs
  17. 28 13
      src/Avalonia.Visuals/Rect.cs
  18. 16 0
      src/Avalonia.Visuals/Size.cs
  19. 4 4
      src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
  20. 2 1
      src/Gtk/Avalonia.Cairo/Media/ImageBrushImpl.cs
  21. 2 1
      src/Gtk/Avalonia.Cairo/Media/LinearGradientBrushImpl.cs
  22. 2 1
      src/Gtk/Avalonia.Cairo/Media/RadialGradientBrushImpl.cs
  23. 1 1
      src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs
  24. 2 1
      src/Gtk/Avalonia.Cairo/Media/VisualBrushImpl.cs
  25. 7 7
      src/Shared/RenderHelpers/TileBrushImplHelper.cs
  26. 4 4
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  27. 6 6
      src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs
  28. 2 1
      src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs
  29. 2 1
      src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs
  30. 3 3
      src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs
  31. 1 2
      src/Windows/Avalonia.Win32/WindowImpl.cs
  32. 2 2
      tests/Avalonia.RenderTests/Media/LinearGradientBrushTests.cs
  33. 2 0
      tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj
  34. 42 0
      tests/Avalonia.Visuals.UnitTests/RectTests.cs
  35. 26 0
      tests/Avalonia.Visuals.UnitTests/SizeTests.cs

+ 2 - 2
src/Avalonia.Controls/Image.cs

@@ -63,10 +63,10 @@ namespace Avalonia.Controls
                 Vector scale = Stretch.CalculateScaling(Bounds.Size, sourceSize);
                 Size scaledSize = sourceSize * scale;
                 Rect destRect = viewPort
-                    .CenterIn(new Rect(scaledSize))
+                    .CenterRect(new Rect(scaledSize))
                     .Intersect(viewPort);
                 Rect sourceRect = new Rect(sourceSize)
-                    .CenterIn(new Rect(destRect.Size / scale));
+                    .CenterRect(new Rect(destRect.Size / scale));
 
                 context.DrawImage(source, 1, sourceRect, destRect);
             }

+ 1 - 1
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@@ -133,7 +133,7 @@ namespace Avalonia.Controls.Presenters
                 return false;
             }
 
-            var rect = targetRect * transform.Value;
+            var rect = targetRect.TransformToAABB(transform.Value);
             var offset = Offset;
             var result = false;
 

+ 1 - 1
src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs

@@ -80,7 +80,7 @@ namespace TheArtOfDev.HtmlRenderer.Avalonia.Adapters
             {
                 StartPoint = new RelativePoint(x, y, RelativeUnit.Relative), 
                 EndPoint = new RelativePoint(1 - x, 1 - y, RelativeUnit.Relative),
-                GradientStops =
+                GradientStops = new[]
                 {
                     new GradientStop(startColor, 0),
                     new GradientStop(endColor, 1)

+ 7 - 1
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@@ -69,7 +69,13 @@
     <Compile Include="Media\BrushMappingMode.cs" />
     <Compile Include="Media\Color.cs" />
     <Compile Include="Media\Colors.cs" />
+    <Compile Include="Media\IGradientBrush.cs" />
+    <Compile Include="Media\ILinearGradientBrush.cs" />
+    <Compile Include="Media\Imaging\IImageBrush.cs" />
     <Compile Include="Media\Imaging\WritableBitmap.cs" />
+    <Compile Include="Media\IRadialGradientBrush.cs" />
+    <Compile Include="Media\ITileBrush.cs" />
+    <Compile Include="Media\IVisualBrush.cs" />
     <Compile Include="Media\TextWrapping.cs" />
     <Compile Include="Media\TransformGroup.cs" />
     <Compile Include="Media\DashStyle.cs" />
@@ -133,7 +139,7 @@
     <Compile Include="Media\TextHitTestResult.cs" />
     <Compile Include="Media\Transform.cs" />
     <Compile Include="Media\TileBrush.cs" />
-    <Compile Include="Media\ImageBush.cs" />
+    <Compile Include="Media\ImageBrush.cs" />
     <Compile Include="Media\VisualBrush.cs" />
     <Compile Include="Platform\IPlatformSettings.cs" />
     <Compile Include="RelativePoint.cs" />

+ 24 - 4
src/Avalonia.Visuals/Media/GradientBrush.cs

@@ -1,32 +1,52 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System;
 using System.Collections.Generic;
 using Avalonia.Metadata;
 
 namespace Avalonia.Media
 {
-    public abstract class GradientBrush : Brush
+    /// <summary>
+    /// Base class for brushes that draw with a gradient.
+    /// </summary>
+    public abstract class GradientBrush : Brush, IGradientBrush
     {
+        /// <summary>
+        /// Defines the <see cref="SpreadMethod"/> property.
+        /// </summary>
         public static readonly StyledProperty<GradientSpreadMethod> SpreadMethodProperty =
             AvaloniaProperty.Register<GradientBrush, GradientSpreadMethod>(nameof(SpreadMethod));
 
-        public static readonly StyledProperty<List<GradientStop>> GradientStopsProperty =
-            AvaloniaProperty.Register<GradientBrush, List<GradientStop>>(nameof(Opacity));
+        /// <summary>
+        /// Defines the <see cref="GradientStops"/> property.
+        /// </summary>
+        public static readonly StyledProperty<IReadOnlyList<GradientStop>> GradientStopsProperty =
+            AvaloniaProperty.Register<GradientBrush, IReadOnlyList<GradientStop>>(nameof(Opacity));
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="GradientBrush"/> class.
+        /// </summary>
         public GradientBrush()
         {
             this.GradientStops = new List<GradientStop>();
         }
 
+        /// <summary>
+        /// Gets or sets the brush's spread method that defines how to draw a gradient that
+        /// doesn't fill the bounds of the destination control.
+        /// </summary>
         public GradientSpreadMethod SpreadMethod
         {
             get { return GetValue(SpreadMethodProperty); }
             set { SetValue(SpreadMethodProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets the brush's gradient stops.
+        /// </summary>
         [Content]
-        public List<GradientStop> GradientStops
+        public IReadOnlyList<GradientStop> GradientStops
         {
             get { return GetValue(GradientStopsProperty); }
             set { SetValue(GradientStopsProperty, value); }

+ 21 - 0
src/Avalonia.Visuals/Media/IGradientBrush.cs

@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// A brush that draws with a gradient.
+    /// </summary>
+    public interface IGradientBrush : IBrush
+    {
+        /// <summary>
+        /// Gets the brush's gradient stops.
+        /// </summary>
+        IReadOnlyList<GradientStop> GradientStops { get; }
+
+        /// <summary>
+        /// Gets the brush's spread method that defines how to draw a gradient that doesn't fill
+        /// the bounds of the destination control.
+        /// </summary>
+        GradientSpreadMethod SpreadMethod { get; }
+    }
+}

+ 18 - 0
src/Avalonia.Visuals/Media/ILinearGradientBrush.cs

@@ -0,0 +1,18 @@
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// A brush that draws with a linear gradient.
+    /// </summary>
+    public interface ILinearGradientBrush : IGradientBrush
+    {
+        /// <summary>
+        /// Gets or sets the start point for the gradient.
+        /// </summary>
+        RelativePoint StartPoint { get; }
+
+        /// <summary>
+        /// Gets or sets the end point for the gradient.
+        /// </summary>
+        RelativePoint EndPoint { get; }
+    }
+}

+ 24 - 0
src/Avalonia.Visuals/Media/IRadialGradientBrush.cs

@@ -0,0 +1,24 @@
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// Paints an area with a radial gradient.
+    /// </summary>
+    public interface IRadialGradientBrush : IGradientBrush
+    {
+        /// <summary>
+        /// Gets the start point for the gradient.
+        /// </summary>
+        RelativePoint Center { get; }
+
+        /// <summary>
+        /// Gets the location of the two-dimensional focal point that defines the beginning of the
+        /// gradient.
+        /// </summary>
+        RelativePoint GradientOrigin { get; }
+
+        /// <summary>
+        /// Gets the horizontal and vertical radius of the outermost circle of the radial gradient.
+        /// </summary>
+        double Radius { get; }
+    }
+}

+ 39 - 0
src/Avalonia.Visuals/Media/ITileBrush.cs

@@ -0,0 +1,39 @@
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// A brush which displays a repeating image.
+    /// </summary>
+    public interface ITileBrush : IBrush
+    {
+        /// <summary>
+        /// Gets the horizontal alignment of a tile in the destination.
+        /// </summary>
+        AlignmentX AlignmentX { get; }
+
+        /// <summary>
+        /// Gets the horizontal alignment of a tile in the destination.
+        /// </summary>
+        AlignmentY AlignmentY { get; }
+
+        /// <summary>
+        /// Gets the rectangle on the destination in which to paint a tile.
+        /// </summary>
+        RelativeRect DestinationRect { get; }
+
+        /// <summary>
+        /// Gets the rectangle of the source image that will be displayed.
+        /// </summary>
+        RelativeRect SourceRect { get; }
+
+        /// <summary>
+        /// Gets a value indicating how the source rectangle will be stretched to fill the
+        /// destination rect.
+        /// </summary>
+        Stretch Stretch { get; }
+
+        /// <summary>
+        /// Gets the brush's tile mode.
+        /// </summary>
+        TileMode TileMode { get; }
+    }
+}

+ 15 - 0
src/Avalonia.Visuals/Media/IVisualBrush.cs

@@ -0,0 +1,15 @@
+using Avalonia.VisualTree;
+
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// Paints an area with an <see cref="IVisual"/>.
+    /// </summary>
+    public interface IVisualBrush : ITileBrush
+    {
+        /// <summary>
+        /// Gets the visual to draw.
+        /// </summary>
+        IVisual Visual { get; }
+    }
+}

+ 1 - 1
src/Avalonia.Visuals/Media/ImageBush.cs → src/Avalonia.Visuals/Media/ImageBrush.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Media
     /// <summary>
     /// Paints an area with an <see cref="IBitmap"/>.
     /// </summary>
-    public class ImageBrush : TileBrush
+    public class ImageBrush : TileBrush, IImageBrush
     {
         /// <summary>
         /// Defines the <see cref="Visual"/> property.

+ 15 - 0
src/Avalonia.Visuals/Media/Imaging/IImageBrush.cs

@@ -0,0 +1,15 @@
+using Avalonia.Media.Imaging;
+
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// Paints an area with an <see cref="IBitmap"/>.
+    /// </summary>
+    public interface IImageBrush : ITileBrush
+    {
+        /// <summary>
+        /// Gets the image to draw.
+        /// </summary>
+        IBitmap Source { get; }
+    }
+}

+ 1 - 1
src/Avalonia.Visuals/Media/LinearGradientBrush.cs

@@ -6,7 +6,7 @@ namespace Avalonia.Media
     /// <summary>
     /// A brush that draws with a linear gradient.
     /// </summary>
-    public sealed class LinearGradientBrush : GradientBrush
+    public sealed class LinearGradientBrush : GradientBrush, ILinearGradientBrush
     {
         /// <summary>
         /// Defines the <see cref="StartPoint"/> property.

+ 5 - 3
src/Avalonia.Visuals/Media/RadialGradientBrush.cs

@@ -7,7 +7,7 @@ namespace Avalonia.Media
     /// Paints an area with a radial gradient. A focal point defines the beginning of the gradient, 
     /// and a circle defines the end point of the gradient.
     /// </summary>
-    public sealed class RadialGradientBrush : GradientBrush
+    public sealed class RadialGradientBrush : GradientBrush, IRadialGradientBrush
     {
         /// <summary>
         /// Defines the <see cref="Center"/> property.
@@ -43,7 +43,8 @@ namespace Avalonia.Media
         }
 
         /// <summary>
-        /// Gets or sets the location of the two-dimensional focal point that defines the beginning of the gradient.
+        /// Gets or sets the location of the two-dimensional focal point that defines the beginning
+        /// of the gradient.
         /// </summary>
         public RelativePoint GradientOrigin
         {
@@ -52,7 +53,8 @@ namespace Avalonia.Media
         }
 
         /// <summary>
-        /// Gets or sets the horizontal and vertical radius of the outermost circle of the radial gradient.
+        /// Gets or sets the horizontal and vertical radius of the outermost circle of the radial
+        /// gradient.
         /// </summary>
         public double Radius
         {

+ 1 - 1
src/Avalonia.Visuals/Media/TileBrush.cs

@@ -37,7 +37,7 @@ namespace Avalonia.Media
     /// <summary>
     /// Base class for brushes which display repeating images.
     /// </summary>
-    public abstract class TileBrush : Brush
+    public abstract class TileBrush : Brush, ITileBrush
     {
         /// <summary>
         /// Defines the <see cref="AlignmentX"/> property.

+ 1 - 1
src/Avalonia.Visuals/Media/VisualBrush.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Media
     /// <summary>
     /// Paints an area with an <see cref="IVisual"/>.
     /// </summary>
-    public class VisualBrush : TileBrush
+    public class VisualBrush : TileBrush, IVisualBrush
     {
         /// <summary>
         /// Defines the <see cref="Visual"/> property.

+ 28 - 13
src/Avalonia.Visuals/Rect.cs

@@ -183,7 +183,7 @@ namespace Avalonia
         }
 
         /// <summary>
-        /// Multiplies a rectangle by a vector.
+        /// Multiplies a rectangle by a scaling vector.
         /// </summary>
         /// <param name="rect">The rectangle.</param>
         /// <param name="scale">The vector scale.</param>
@@ -197,17 +197,6 @@ namespace Avalonia
                 rect.Height * scale.Y);
         }
 
-        /// <summary>
-        /// Transforms a rectangle by a matrix and returns the axis-aligned bounding box.
-        /// </summary>
-        /// <param name="rect">The rectangle.</param>
-        /// <param name="matrix">The matrix.</param>
-        /// <returns>The axis-aligned bounding box.</returns>
-        public static Rect operator *(Rect rect, Matrix matrix)
-        {
-            return new Rect(rect.TopLeft * matrix, rect.BottomRight * matrix);
-        }
-
         /// <summary>
         /// Divides a rectangle by a vector.
         /// </summary>
@@ -249,7 +238,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="rect">The rectangle to center.</param>
         /// <returns>The centered rectangle.</returns>
-        public Rect CenterIn(Rect rect)
+        public Rect CenterRect(Rect rect)
         {
             return new Rect(
                 _x + ((_width - rect._width) / 2),
@@ -412,6 +401,32 @@ namespace Avalonia
             return new Rect(Position + offset, Size);
         }
 
+        /// <summary>
+        /// Gets the union of two rectangles.
+        /// </summary>
+        /// <param name="rect">The other rectangle.</param>
+        /// <returns>The union.</returns>
+        public Rect Union(Rect rect)
+        {
+            if (IsEmpty)
+            {
+                return rect;
+            }
+            else if (rect.IsEmpty)
+            {
+                return this;
+            }
+            else
+            {
+                var x1 = Math.Min(this.X, rect.X);
+                var x2 = Math.Max(this.Right, rect.Right);
+                var y1 = Math.Min(this.Y, rect.Y);
+                var y2 = Math.Max(this.Bottom, rect.Bottom);
+
+                return new Rect(new Point(x1, y1), new Point(x2, y2));
+            }
+        }
+
         /// <summary>
         /// Returns a new <see cref="Rect"/> with the specified X position.
         /// </summary>

+ 16 - 0
src/Avalonia.Visuals/Size.cs

@@ -43,6 +43,11 @@ namespace Avalonia
             _height = height;
         }
 
+        /// <summary>
+        /// Gets the aspect ratio of the size.
+        /// </summary>
+        public double AspectRatio => _width / _height;
+
         /// <summary>
         /// Gets the width.
         /// </summary>
@@ -97,6 +102,17 @@ namespace Avalonia
             return new Size(size._width / scale.X, size._height / scale.Y);
         }
 
+        /// <summary>
+        /// Divides a size by another size to produce a scaling factor.
+        /// </summary>
+        /// <param name="left">The first size</param>
+        /// <param name="right">The second size.</param>
+        /// <returns>The scaled size.</returns>
+        public static Vector operator /(Size left, Size right)
+        {
+            return new Vector(left._width / right._width, left._height / right._height);
+        }
+
         /// <summary>
         /// Scales a size.
         /// </summary>

+ 4 - 4
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

@@ -290,10 +290,10 @@ namespace Avalonia.Cairo.Media
         private BrushImpl CreateBrushImpl(IBrush brush, Size destinationSize)
         {
             var solid = brush as SolidColorBrush;
-            var linearGradientBrush = brush as LinearGradientBrush;
-            var radialGradientBrush = brush as RadialGradientBrush;
-            var imageBrush = brush as ImageBrush;
-            var visualBrush = brush as VisualBrush;
+            var linearGradientBrush = brush as ILinearGradientBrush;
+            var radialGradientBrush = brush as IRadialGradientBrush;
+            var imageBrush = brush as IImageBrush;
+            var visualBrush = brush as IVisualBrush;
             BrushImpl impl = null;
 
             if (solid != null)

+ 2 - 1
src/Gtk/Avalonia.Cairo/Media/ImageBrushImpl.cs

@@ -1,11 +1,12 @@
 using System;
+using Avalonia.Media;
 using global::Cairo;
 
 namespace Avalonia.Cairo.Media
 {
 	public class ImageBrushImpl : BrushImpl
 	{
-		public ImageBrushImpl(Avalonia.Media.ImageBrush brush, Size destinationSize)
+		public ImageBrushImpl(IImageBrush brush, Size destinationSize)
 		{
 			this.PlatformBrush = TileBrushes.CreateTileBrush(brush, destinationSize);
 		}

+ 2 - 1
src/Gtk/Avalonia.Cairo/Media/LinearGradientBrushImpl.cs

@@ -1,11 +1,12 @@
 using System;
+using Avalonia.Media;
 using global::Cairo;
 
 namespace Avalonia.Cairo
 {
 	public class LinearGradientBrushImpl : BrushImpl
 	{
-		public LinearGradientBrushImpl(Avalonia.Media.LinearGradientBrush brush, Size destinationSize)
+		public LinearGradientBrushImpl(ILinearGradientBrush brush, Size destinationSize)
 		{
 			var start = brush.StartPoint.ToPixels(destinationSize);
 			var end = brush.EndPoint.ToPixels(destinationSize);

+ 2 - 1
src/Gtk/Avalonia.Cairo/Media/RadialGradientBrushImpl.cs

@@ -1,11 +1,12 @@
 using System;
+using Avalonia.Media;
 using global::Cairo;
 
 namespace Avalonia.Cairo
 {
 	public class RadialGradientBrushImpl : BrushImpl
 	{
-		public RadialGradientBrushImpl(Avalonia.Media.RadialGradientBrush brush, Size destinationSize)
+		public RadialGradientBrushImpl(IRadialGradientBrush brush, Size destinationSize)
 		{
 			var center = brush.Center.ToPixels(destinationSize);
 			var gradientOrigin = brush.GradientOrigin.ToPixels(destinationSize);

+ 1 - 1
src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Cairo.Media
 {
     internal static class TileBrushes
     {
-        public static SurfacePattern CreateTileBrush(TileBrush brush, Size targetSize)
+        public static SurfacePattern CreateTileBrush(ITileBrush brush, Size targetSize)
         {
             var helper = new TileBrushImplHelper(brush, targetSize);
             if (!helper.IsValid)

+ 2 - 1
src/Gtk/Avalonia.Cairo/Media/VisualBrushImpl.cs

@@ -1,11 +1,12 @@
 using System;
+using Avalonia.Media;
 using global::Cairo;
 
 namespace Avalonia.Cairo.Media
 {
 	public class VisualBrushImpl : BrushImpl
 	{
-		public VisualBrushImpl(Avalonia.Media.VisualBrush brush, Size destinationSize)
+		public VisualBrushImpl(IVisualBrush brush, Size destinationSize)
 		{
 			this.PlatformBrush = TileBrushes.CreateTileBrush(brush, destinationSize);
 		}

+ 7 - 7
src/Shared/RenderHelpers/TileBrushImplHelper.cs

@@ -17,17 +17,17 @@ namespace Avalonia.RenderHelpers
         private readonly Vector _scale;
         private readonly Vector _translate;
         private readonly Size _imageSize;
-        private readonly VisualBrush _visualBrush;
-        private readonly ImageBrush _imageBrush;
+        private readonly IVisualBrush _visualBrush;
+        private readonly IImageBrush _imageBrush;
         private readonly Matrix _transform;
         private readonly Rect _drawRect;
 
         public bool IsValid { get; }
         
-        public TileBrushImplHelper(TileBrush brush, Size targetSize)
+        public TileBrushImplHelper(ITileBrush brush, Size targetSize)
         {
-            _imageBrush = brush as ImageBrush;
-            _visualBrush = brush as VisualBrush;
+            _imageBrush = brush as IImageBrush;
+            _visualBrush = brush as IVisualBrush;
             if (_imageBrush != null)
             {
                 if (_imageBrush.Source == null)
@@ -112,7 +112,7 @@ namespace Avalonia.RenderHelpers
 
 
         /// <summary>
-        /// Calculates a translate based on a <see cref="TileBrush"/>, a source and destination
+        /// Calculates a translate based on an <see cref="ITileBrush"/>, a source and destination
         /// rectangle and a scale.
         /// </summary>
         /// <param name="brush">The brush.</param>
@@ -122,7 +122,7 @@ namespace Avalonia.RenderHelpers
         /// <returns>A vector with the X and Y _translate.</returns>
 
         public static Vector CalculateTranslate(
-            TileBrush brush,
+            ITileBrush brush,
             Rect sourceRect,
             Rect destinationRect,
             Vector scale)

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

@@ -148,14 +148,14 @@ namespace Avalonia.Skia
                 return rv;
             }
 
-            var gradient = brush as GradientBrush;
+            var gradient = brush as IGradientBrush;
             if (gradient != null)
             {
                 var tileMode = gradient.SpreadMethod.ToSKShaderTileMode();
                 var stopColors = gradient.GradientStops.Select(s => s.Color.ToSKColor()).ToArray();
                 var stopOffsets = gradient.GradientStops.Select(s => (float)s.Offset).ToArray();
 
-                var linearGradient = brush as LinearGradientBrush;
+                var linearGradient = brush as ILinearGradientBrush;
                 if (linearGradient != null)
                 {
                     var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint();
@@ -168,7 +168,7 @@ namespace Avalonia.Skia
                 }
                 else
                 {
-                    var radialGradient = brush as RadialGradientBrush;
+                    var radialGradient = brush as IRadialGradientBrush;
                     if (radialGradient != null)
                     {
                         var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint();
@@ -187,7 +187,7 @@ namespace Avalonia.Skia
                 return rv;
             }
 
-            var tileBrush = brush as TileBrush;
+            var tileBrush = brush as ITileBrush;
             if (tileBrush != null)
             {
                 var helper = new TileBrushImplHelper(tileBrush, targetSize);

+ 6 - 6
src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs

@@ -311,11 +311,11 @@ namespace Avalonia.Direct2D1.Media
         /// <returns>The Direct2D brush wrapper.</returns>
         public BrushImpl CreateBrush(IBrush brush, Size destinationSize)
         {
-            var solidColorBrush = brush as Avalonia.Media.ISolidColorBrush;
-            var linearGradientBrush = brush as Avalonia.Media.LinearGradientBrush;
-            var radialGradientBrush = brush as Avalonia.Media.RadialGradientBrush;
-            var imageBrush = brush as Avalonia.Media.ImageBrush;
-            var visualBrush = brush as Avalonia.Media.VisualBrush;
+            var solidColorBrush = brush as ISolidColorBrush;
+            var linearGradientBrush = brush as ILinearGradientBrush;
+            var radialGradientBrush = brush as IRadialGradientBrush;
+            var imageBrush = brush as IImageBrush;
+            var visualBrush = brush as IVisualBrush;
 
             if (solidColorBrush != null)
             {
@@ -339,7 +339,7 @@ namespace Avalonia.Direct2D1.Media
             }
             else
             {
-                return new SolidColorBrushImpl((Avalonia.Media.SolidColorBrush)null, _renderTarget);
+                return new SolidColorBrushImpl(null, _renderTarget);
             }
         }
 

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

@@ -2,13 +2,14 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System.Linq;
+using Avalonia.Media;
 
 namespace Avalonia.Direct2D1.Media
 {
     public class LinearGradientBrushImpl : BrushImpl
     {
         public LinearGradientBrushImpl(
-            Avalonia.Media.LinearGradientBrush brush,
+            ILinearGradientBrush brush,
             SharpDX.Direct2D1.RenderTarget target,
             Size destinationSize)
         {

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

@@ -2,13 +2,14 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System.Linq;
+using Avalonia.Media;
 
 namespace Avalonia.Direct2D1.Media
 {
     public class RadialGradientBrushImpl : BrushImpl
     {
         public RadialGradientBrushImpl(
-            Avalonia.Media.RadialGradientBrush brush,
+            IRadialGradientBrush brush,
             SharpDX.Direct2D1.RenderTarget target,
             Size destinationSize)
         {

+ 3 - 3
src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Direct2D1.Media
     public sealed class TileBrushImpl : BrushImpl
     {
         public TileBrushImpl(
-            TileBrush brush,
+            ITileBrush brush,
             SharpDX.Direct2D1.RenderTarget target,
             Size targetSize)
         {
@@ -34,7 +34,7 @@ namespace Avalonia.Direct2D1.Media
             }
         }
 
-        private static BrushProperties GetBrushProperties(TileBrush brush, Rect destinationRect)
+        private static BrushProperties GetBrushProperties(ITileBrush brush, Rect destinationRect)
         {
             var tileTransform = 
                 brush.TileMode != TileMode.None ? 
@@ -48,7 +48,7 @@ namespace Avalonia.Direct2D1.Media
             };
         }
 
-        private static BitmapBrushProperties GetBitmapBrushProperties(TileBrush brush)
+        private static BitmapBrushProperties GetBitmapBrushProperties(ITileBrush brush)
         {
             var tileMode = brush.TileMode;
 

+ 1 - 2
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -572,9 +572,8 @@ namespace Avalonia.Win32
 
                     if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero)
                     {
-                        UnmanagedMethods.RECT r;
-                        UnmanagedMethods.GetUpdateRect(_hwnd, out r, false);
                         var f = Scaling;
+                        var r = ps.rcPaint;
                         Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f, (r.bottom - r.top) / f));
                         UnmanagedMethods.EndPaint(_hwnd, ref ps);
                     }

+ 2 - 2
tests/Avalonia.RenderTests/Media/LinearGradientBrushTests.cs

@@ -42,7 +42,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
                     {
                         StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative),
                         EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative),
-                        GradientStops =
+                        GradientStops = new[]
                         {
                             new GradientStop { Color = Colors.Red, Offset = 0 },
                             new GradientStop { Color = Colors.Blue, Offset = 1 }
@@ -73,7 +73,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
                     {
                         StartPoint = new RelativePoint(0.5, 0, RelativeUnit.Relative),
                         EndPoint = new RelativePoint(0.5, 1, RelativeUnit.Relative),
-                        GradientStops =
+                        GradientStops = new[]
                         {
                             new GradientStop { Color = Colors.Red, Offset = 0 },
                             new GradientStop { Color = Colors.Blue, Offset = 1 }

+ 2 - 0
tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj

@@ -80,6 +80,8 @@
     <Compile Include="Media\FormattedTextTests.cs" />
     <Compile Include="Media\PathMarkupParserTests.cs" />
     <Compile Include="RelativeRectComparer.cs" />
+    <Compile Include="SizeTests.cs" />
+    <Compile Include="RectTests.cs" />
     <Compile Include="RelativeRectTests.cs" />
     <Compile Include="ThicknessTests.cs" />
     <Compile Include="Media\BrushTests.cs" />

+ 42 - 0
tests/Avalonia.Visuals.UnitTests/RectTests.cs

@@ -0,0 +1,42 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Xunit;
+
+namespace Avalonia.Visuals.UnitTests
+{
+    public class RectTests
+    {
+        [Fact]
+        public void Union_Should_Return_Correct_Value_For_Intersecting_Rects()
+        {
+            var result = new Rect(0, 0, 100, 100).Union(new Rect(50, 50, 100, 100));
+
+            Assert.Equal(new Rect(0, 0, 150, 150), result);
+        }
+
+        [Fact]
+        public void Union_Should_Return_Correct_Value_For_NonIntersecting_Rects()
+        {
+            var result = new Rect(0, 0, 100, 100).Union(new Rect(150, 150, 100, 100));
+
+            Assert.Equal(new Rect(0, 0, 250, 250), result);
+        }
+
+        [Fact]
+        public void Union_Should_Ignore_Empty_This_rect()
+        {
+            var result = new Rect(0, 0, 0, 0).Union(new Rect(150, 150, 100, 100));
+
+            Assert.Equal(new Rect(150, 150, 100, 100), result);
+        }
+
+        [Fact]
+        public void Union_Should_Ignore_Empty_Other_rect()
+        {
+            var result = new Rect(0, 0, 100, 100).Union(new Rect(150, 150, 0, 0));
+
+            Assert.Equal(new Rect(0, 0, 100, 100), result);
+        }
+    }
+}

+ 26 - 0
tests/Avalonia.Visuals.UnitTests/SizeTests.cs

@@ -0,0 +1,26 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Xunit;
+
+namespace Avalonia.Visuals.UnitTests
+{
+    public class SizeTests
+    {
+        [Fact]
+        public void Should_Produce_Correct_Aspect_Ratio()
+        {
+            var result = new Size(3, 2).AspectRatio;
+
+            Assert.Equal(1.5, result);
+        }
+
+        [Fact]
+        public void Dividing_Should_Produce_Scaling_Factor()
+        {
+            var result = new Size(15, 10) / new Size(5, 5);
+
+            Assert.Equal(new Vector(3, 2), result);
+        }
+    }
+}