From f6dee9eab4cb710e4cdc820ba671d40493cf0a2e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 5 Mar 2017 02:40:04 +0100 Subject: [PATCH 1/8] Add interfaces for all brush types. Also: - Change `GradientBrush.GradientStops` to be a readonly list, as changes to it would have been ignored anyway. - Fix typo in ImageBrush filename. --- .../Adapters/AvaloniaAdapter.cs | 2 +- src/Avalonia.Visuals/Avalonia.Visuals.csproj | 8 +++- src/Avalonia.Visuals/Media/GradientBrush.cs | 28 +++++++++++-- src/Avalonia.Visuals/Media/IGradientBrush.cs | 21 ++++++++++ .../Media/ILinearGradientBrush.cs | 18 +++++++++ .../Media/IRadialGradientBrush.cs | 24 ++++++++++++ src/Avalonia.Visuals/Media/ITileBrush.cs | 39 +++++++++++++++++++ src/Avalonia.Visuals/Media/IVisualBrush.cs | 15 +++++++ .../Media/{ImageBush.cs => ImageBrush.cs} | 2 +- .../Media/Imaging/IImageBrush.cs | 15 +++++++ .../Media/LinearGradientBrush.cs | 2 +- .../Media/RadialGradientBrush.cs | 8 ++-- src/Avalonia.Visuals/Media/TileBrush.cs | 2 +- src/Avalonia.Visuals/Media/VisualBrush.cs | 2 +- .../Media/LinearGradientBrushTests.cs | 4 +- 15 files changed, 175 insertions(+), 15 deletions(-) create mode 100644 src/Avalonia.Visuals/Media/IGradientBrush.cs create mode 100644 src/Avalonia.Visuals/Media/ILinearGradientBrush.cs create mode 100644 src/Avalonia.Visuals/Media/IRadialGradientBrush.cs create mode 100644 src/Avalonia.Visuals/Media/ITileBrush.cs create mode 100644 src/Avalonia.Visuals/Media/IVisualBrush.cs rename src/Avalonia.Visuals/Media/{ImageBush.cs => ImageBrush.cs} (95%) create mode 100644 src/Avalonia.Visuals/Media/Imaging/IImageBrush.cs diff --git a/src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs b/src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs index 12c040fab3..aab65fcbc7 100644 --- a/src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs +++ b/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) diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index 812e9d48ad..d5a8a261bd 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -69,7 +69,13 @@ + + + + + + @@ -133,7 +139,7 @@ - + diff --git a/src/Avalonia.Visuals/Media/GradientBrush.cs b/src/Avalonia.Visuals/Media/GradientBrush.cs index 4c62a2b836..52edf12e7f 100644 --- a/src/Avalonia.Visuals/Media/GradientBrush.cs +++ b/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 + /// + /// Base class for brushes that draw with a gradient. + /// + public abstract class GradientBrush : Brush, IGradientBrush { + /// + /// Defines the property. + /// public static readonly StyledProperty SpreadMethodProperty = AvaloniaProperty.Register(nameof(SpreadMethod)); - public static readonly StyledProperty> GradientStopsProperty = - AvaloniaProperty.Register>(nameof(Opacity)); + /// + /// Defines the property. + /// + public static readonly StyledProperty> GradientStopsProperty = + AvaloniaProperty.Register>(nameof(Opacity)); + /// + /// Initializes a new instance of the class. + /// public GradientBrush() { this.GradientStops = new List(); } + /// + /// 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. + /// public GradientSpreadMethod SpreadMethod { get { return GetValue(SpreadMethodProperty); } set { SetValue(SpreadMethodProperty, value); } } + /// + /// Gets or sets the brush's gradient stops. + /// [Content] - public List GradientStops + public IReadOnlyList GradientStops { get { return GetValue(GradientStopsProperty); } set { SetValue(GradientStopsProperty, value); } diff --git a/src/Avalonia.Visuals/Media/IGradientBrush.cs b/src/Avalonia.Visuals/Media/IGradientBrush.cs new file mode 100644 index 0000000000..ce064c4a1f --- /dev/null +++ b/src/Avalonia.Visuals/Media/IGradientBrush.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace Avalonia.Media +{ + /// + /// A brush that draws with a gradient. + /// + public interface IGradientBrush : IBrush + { + /// + /// Gets the brush's gradient stops. + /// + IReadOnlyList GradientStops { get; } + + /// + /// Gets the brush's spread method that defines how to draw a gradient that doesn't fill + /// the bounds of the destination control. + /// + GradientSpreadMethod SpreadMethod { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/ILinearGradientBrush.cs b/src/Avalonia.Visuals/Media/ILinearGradientBrush.cs new file mode 100644 index 0000000000..3e2a5a0e22 --- /dev/null +++ b/src/Avalonia.Visuals/Media/ILinearGradientBrush.cs @@ -0,0 +1,18 @@ +namespace Avalonia.Media +{ + /// + /// A brush that draws with a linear gradient. + /// + public interface ILinearGradientBrush : IGradientBrush + { + /// + /// Gets or sets the start point for the gradient. + /// + RelativePoint StartPoint { get; } + + /// + /// Gets or sets the end point for the gradient. + /// + RelativePoint EndPoint { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/IRadialGradientBrush.cs b/src/Avalonia.Visuals/Media/IRadialGradientBrush.cs new file mode 100644 index 0000000000..cadf53cc18 --- /dev/null +++ b/src/Avalonia.Visuals/Media/IRadialGradientBrush.cs @@ -0,0 +1,24 @@ +namespace Avalonia.Media +{ + /// + /// Paints an area with a radial gradient. + /// + public interface IRadialGradientBrush : IGradientBrush + { + /// + /// Gets the start point for the gradient. + /// + RelativePoint Center { get; } + + /// + /// Gets the location of the two-dimensional focal point that defines the beginning of the + /// gradient. + /// + RelativePoint GradientOrigin { get; } + + /// + /// Gets the horizontal and vertical radius of the outermost circle of the radial gradient. + /// + double Radius { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/ITileBrush.cs b/src/Avalonia.Visuals/Media/ITileBrush.cs new file mode 100644 index 0000000000..8e2349f506 --- /dev/null +++ b/src/Avalonia.Visuals/Media/ITileBrush.cs @@ -0,0 +1,39 @@ +namespace Avalonia.Media +{ + /// + /// A brush which displays a repeating image. + /// + public interface ITileBrush : IBrush + { + /// + /// Gets the horizontal alignment of a tile in the destination. + /// + AlignmentX AlignmentX { get; } + + /// + /// Gets the horizontal alignment of a tile in the destination. + /// + AlignmentY AlignmentY { get; } + + /// + /// Gets the rectangle on the destination in which to paint a tile. + /// + RelativeRect DestinationRect { get; } + + /// + /// Gets the rectangle of the source image that will be displayed. + /// + RelativeRect SourceRect { get; } + + /// + /// Gets a value indicating how the source rectangle will be stretched to fill the + /// destination rect. + /// + Stretch Stretch { get; } + + /// + /// Gets the brush's tile mode. + /// + TileMode TileMode { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/IVisualBrush.cs b/src/Avalonia.Visuals/Media/IVisualBrush.cs new file mode 100644 index 0000000000..e74892b218 --- /dev/null +++ b/src/Avalonia.Visuals/Media/IVisualBrush.cs @@ -0,0 +1,15 @@ +using Avalonia.VisualTree; + +namespace Avalonia.Media +{ + /// + /// Paints an area with an . + /// + public interface IVisualBrush : ITileBrush + { + /// + /// Gets the visual to draw. + /// + IVisual Visual { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/ImageBush.cs b/src/Avalonia.Visuals/Media/ImageBrush.cs similarity index 95% rename from src/Avalonia.Visuals/Media/ImageBush.cs rename to src/Avalonia.Visuals/Media/ImageBrush.cs index caf43d19a2..85c1f6c71a 100644 --- a/src/Avalonia.Visuals/Media/ImageBush.cs +++ b/src/Avalonia.Visuals/Media/ImageBrush.cs @@ -8,7 +8,7 @@ namespace Avalonia.Media /// /// Paints an area with an . /// - public class ImageBrush : TileBrush + public class ImageBrush : TileBrush, IImageBrush { /// /// Defines the property. diff --git a/src/Avalonia.Visuals/Media/Imaging/IImageBrush.cs b/src/Avalonia.Visuals/Media/Imaging/IImageBrush.cs new file mode 100644 index 0000000000..aaa481bd28 --- /dev/null +++ b/src/Avalonia.Visuals/Media/Imaging/IImageBrush.cs @@ -0,0 +1,15 @@ +using Avalonia.Media.Imaging; + +namespace Avalonia.Media +{ + /// + /// Paints an area with an . + /// + public interface IImageBrush : ITileBrush + { + /// + /// Gets the image to draw. + /// + IBitmap Source { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/LinearGradientBrush.cs b/src/Avalonia.Visuals/Media/LinearGradientBrush.cs index 6ba2c5093e..33eea4fbad 100644 --- a/src/Avalonia.Visuals/Media/LinearGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/LinearGradientBrush.cs @@ -6,7 +6,7 @@ namespace Avalonia.Media /// /// A brush that draws with a linear gradient. /// - public sealed class LinearGradientBrush : GradientBrush + public sealed class LinearGradientBrush : GradientBrush, ILinearGradientBrush { /// /// Defines the property. diff --git a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs b/src/Avalonia.Visuals/Media/RadialGradientBrush.cs index 70657abe88..b721e19138 100644 --- a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs +++ b/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. /// - public sealed class RadialGradientBrush : GradientBrush + public sealed class RadialGradientBrush : GradientBrush, IRadialGradientBrush { /// /// Defines the property. @@ -43,7 +43,8 @@ namespace Avalonia.Media } /// - /// 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. /// public RelativePoint GradientOrigin { @@ -52,7 +53,8 @@ namespace Avalonia.Media } /// - /// 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. /// public double Radius { diff --git a/src/Avalonia.Visuals/Media/TileBrush.cs b/src/Avalonia.Visuals/Media/TileBrush.cs index 1708c7e9f6..3a7f9d9920 100644 --- a/src/Avalonia.Visuals/Media/TileBrush.cs +++ b/src/Avalonia.Visuals/Media/TileBrush.cs @@ -37,7 +37,7 @@ namespace Avalonia.Media /// /// Base class for brushes which display repeating images. /// - public abstract class TileBrush : Brush + public abstract class TileBrush : Brush, ITileBrush { /// /// Defines the property. diff --git a/src/Avalonia.Visuals/Media/VisualBrush.cs b/src/Avalonia.Visuals/Media/VisualBrush.cs index 41a61f6022..85ccf7afc9 100644 --- a/src/Avalonia.Visuals/Media/VisualBrush.cs +++ b/src/Avalonia.Visuals/Media/VisualBrush.cs @@ -8,7 +8,7 @@ namespace Avalonia.Media /// /// Paints an area with an . /// - public class VisualBrush : TileBrush + public class VisualBrush : TileBrush, IVisualBrush { /// /// Defines the property. diff --git a/tests/Avalonia.RenderTests/Media/LinearGradientBrushTests.cs b/tests/Avalonia.RenderTests/Media/LinearGradientBrushTests.cs index 104edf04e5..6c26d067a7 100644 --- a/tests/Avalonia.RenderTests/Media/LinearGradientBrushTests.cs +++ b/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 } From 74a0faa637f2f99cdad863157cf12dadfdc2d7c6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 5 Mar 2017 02:50:17 +0100 Subject: [PATCH 2/8] Use brush interfaces in rendering backends. --- src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs | 8 ++++---- src/Gtk/Avalonia.Cairo/Media/ImageBrushImpl.cs | 3 ++- .../Media/LinearGradientBrushImpl.cs | 3 ++- .../Media/RadialGradientBrushImpl.cs | 3 ++- src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs | 2 +- src/Gtk/Avalonia.Cairo/Media/VisualBrushImpl.cs | 3 ++- src/Shared/RenderHelpers/TileBrushImplHelper.cs | 14 +++++++------- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 8 ++++---- .../Avalonia.Direct2D1/Media/DrawingContext.cs | 12 ++++++------ .../Media/LinearGradientBrushImpl.cs | 3 ++- .../Media/RadialGradientBrushImpl.cs | 3 ++- .../Avalonia.Direct2D1/Media/TileBrushImpl.cs | 6 +++--- 12 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs index 9ec37d5514..c9d07ad0cb 100644 --- a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs +++ b/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) diff --git a/src/Gtk/Avalonia.Cairo/Media/ImageBrushImpl.cs b/src/Gtk/Avalonia.Cairo/Media/ImageBrushImpl.cs index 486ad50b1f..0ee7c630aa 100644 --- a/src/Gtk/Avalonia.Cairo/Media/ImageBrushImpl.cs +++ b/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); } diff --git a/src/Gtk/Avalonia.Cairo/Media/LinearGradientBrushImpl.cs b/src/Gtk/Avalonia.Cairo/Media/LinearGradientBrushImpl.cs index c809e5d2da..1e82da4059 100644 --- a/src/Gtk/Avalonia.Cairo/Media/LinearGradientBrushImpl.cs +++ b/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); diff --git a/src/Gtk/Avalonia.Cairo/Media/RadialGradientBrushImpl.cs b/src/Gtk/Avalonia.Cairo/Media/RadialGradientBrushImpl.cs index 3fcb9c8244..52460c9628 100644 --- a/src/Gtk/Avalonia.Cairo/Media/RadialGradientBrushImpl.cs +++ b/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); diff --git a/src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs b/src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs index b533210749..eb78c9d2f3 100644 --- a/src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs +++ b/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) diff --git a/src/Gtk/Avalonia.Cairo/Media/VisualBrushImpl.cs b/src/Gtk/Avalonia.Cairo/Media/VisualBrushImpl.cs index 7c0e59e4d9..e820c50420 100644 --- a/src/Gtk/Avalonia.Cairo/Media/VisualBrushImpl.cs +++ b/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); } diff --git a/src/Shared/RenderHelpers/TileBrushImplHelper.cs b/src/Shared/RenderHelpers/TileBrushImplHelper.cs index de26b6ca36..ad1dc44c61 100644 --- a/src/Shared/RenderHelpers/TileBrushImplHelper.cs +++ b/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 /// - /// Calculates a translate based on a , a source and destination + /// Calculates a translate based on an , a source and destination /// rectangle and a scale. /// /// The brush. @@ -122,7 +122,7 @@ namespace Avalonia.RenderHelpers /// A vector with the X and Y _translate. public static Vector CalculateTranslate( - TileBrush brush, + ITileBrush brush, Rect sourceRect, Rect destinationRect, Vector scale) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 0b1ef3fea7..10ce1d1c91 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/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); diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs index 9e90d08280..51925d5341 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs @@ -311,11 +311,11 @@ namespace Avalonia.Direct2D1.Media /// The Direct2D brush wrapper. 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); } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs index 8d8058340e..5369f84f46 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs +++ b/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) { diff --git a/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs index 72779096ff..85417fe918 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs +++ b/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) { diff --git a/src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs index dd1aac3776..b0c4a2c83b 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs +++ b/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; From c6b3440e70798ccee29d7a19bc8260388ed76b02 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 11 Jan 2017 14:55:09 +0100 Subject: [PATCH 3/8] Remove Rect.operator* It was wrong - use Rect.TransformToAABB instead. --- .../Presenters/ScrollContentPresenter.cs | 2 +- src/Avalonia.Visuals/Rect.cs | 13 +------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index d9f0260e39..c217290226 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/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; diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index 10253ec829..0d6aadbcaa 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -183,7 +183,7 @@ namespace Avalonia } /// - /// Multiplies a rectangle by a vector. + /// Multiplies a rectangle by a scaling vector. /// /// The rectangle. /// The vector scale. @@ -197,17 +197,6 @@ namespace Avalonia rect.Height * scale.Y); } - /// - /// Transforms a rectangle by a matrix and returns the axis-aligned bounding box. - /// - /// The rectangle. - /// The matrix. - /// The axis-aligned bounding box. - public static Rect operator *(Rect rect, Matrix matrix) - { - return new Rect(rect.TopLeft * matrix, rect.BottomRight * matrix); - } - /// /// Divides a rectangle by a vector. /// From 6cc829a132dd41fd8541a5ad5485091108886d1e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 5 Mar 2017 20:33:24 +0100 Subject: [PATCH 4/8] Renamed Rect.CenterIn -> CenterRect. The previous naming suggested that the `Rect` that the method was being called on was the one being centered. --- src/Avalonia.Controls/Image.cs | 4 ++-- src/Avalonia.Visuals/Rect.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 43c8fbcbb4..f6f11aa9ad 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/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); } diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index 0d6aadbcaa..8357400f33 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -238,7 +238,7 @@ namespace Avalonia /// /// The rectangle to center. /// The centered rectangle. - public Rect CenterIn(Rect rect) + public Rect CenterRect(Rect rect) { return new Rect( _x + ((_width - rect._width) / 2), From 5546ed043ff1b3ae2d8c0538f9fd4afcdbc883c7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 5 Mar 2017 20:37:14 +0100 Subject: [PATCH 5/8] Added Rect.Union. --- src/Avalonia.Visuals/Rect.cs | 26 +++++++++++++++++++ .../Avalonia.Visuals.UnitTests.csproj | 1 + tests/Avalonia.Visuals.UnitTests/RectTests.cs | 26 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 tests/Avalonia.Visuals.UnitTests/RectTests.cs diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index 8357400f33..0132c5e8a3 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -401,6 +401,32 @@ namespace Avalonia return new Rect(Position + offset, Size); } + /// + /// Gets the union of two rectangles. + /// + /// The other rectangle. + /// The union. + 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)); + } + } + /// /// Returns a new with the specified X position. /// diff --git a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj index 9e7f739a1c..b5b6dbd404 100644 --- a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj +++ b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj @@ -80,6 +80,7 @@ + diff --git a/tests/Avalonia.Visuals.UnitTests/RectTests.cs b/tests/Avalonia.Visuals.UnitTests/RectTests.cs new file mode 100644 index 0000000000..bd004eda19 --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/RectTests.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 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); + } + } +} From 64e88961447cf3a4ab26fa273e8b6a9969314557 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 5 Mar 2017 20:41:48 +0100 Subject: [PATCH 6/8] Added Rect.Union empty tests. --- tests/Avalonia.Visuals.UnitTests/RectTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/Avalonia.Visuals.UnitTests/RectTests.cs b/tests/Avalonia.Visuals.UnitTests/RectTests.cs index bd004eda19..6e3f654bfb 100644 --- a/tests/Avalonia.Visuals.UnitTests/RectTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/RectTests.cs @@ -22,5 +22,21 @@ namespace Avalonia.Visuals.UnitTests 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); + } } } From 2b87e22db4d321eb1b519d7539a23526a45a9b94 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 5 Mar 2017 20:51:11 +0100 Subject: [PATCH 7/8] Use the dirty rect from BeginPaint on WM_PAINT. The one returned by `GetUpdateRect` at this point will be empty, from the win32 docs: > BeginPaint automatically validates the update region, so any call to GetUpdateRect made immediately after the call to BeginPaint retrieves an empty update region. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 0000b5c0a9..f80e62ee2d 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/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); } From 609c571424f1cd5de27960b9775e1af2cf2bd9f7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 5 Mar 2017 21:03:22 +0100 Subject: [PATCH 8/8] Added AspectRatio and division operator to Size. --- src/Avalonia.Visuals/Size.cs | 16 ++++++++++++ .../Avalonia.Visuals.UnitTests.csproj | 1 + tests/Avalonia.Visuals.UnitTests/SizeTests.cs | 26 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 tests/Avalonia.Visuals.UnitTests/SizeTests.cs diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Visuals/Size.cs index a11411c3ee..6ad87c6120 100644 --- a/src/Avalonia.Visuals/Size.cs +++ b/src/Avalonia.Visuals/Size.cs @@ -43,6 +43,11 @@ namespace Avalonia _height = height; } + /// + /// Gets the aspect ratio of the size. + /// + public double AspectRatio => _width / _height; + /// /// Gets the width. /// @@ -97,6 +102,17 @@ namespace Avalonia return new Size(size._width / scale.X, size._height / scale.Y); } + /// + /// Divides a size by another size to produce a scaling factor. + /// + /// The first size + /// The second size. + /// The scaled size. + public static Vector operator /(Size left, Size right) + { + return new Vector(left._width / right._width, left._height / right._height); + } + /// /// Scales a size. /// diff --git a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj index 9e7f739a1c..3024b226c1 100644 --- a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj +++ b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj @@ -80,6 +80,7 @@ + diff --git a/tests/Avalonia.Visuals.UnitTests/SizeTests.cs b/tests/Avalonia.Visuals.UnitTests/SizeTests.cs new file mode 100644 index 0000000000..10a012bb7f --- /dev/null +++ b/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); + } + } +}