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/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
index 11860f3ea3..35aaada8b8 100644
--- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj
+++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
@@ -135,6 +135,7 @@
+
diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs
index dec1c79df8..0132c5e8a3 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),
diff --git a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs
new file mode 100644
index 0000000000..5a8adc5c45
--- /dev/null
+++ b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs
@@ -0,0 +1,193 @@
+// 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 Avalonia.Media;
+
+namespace Avalonia.Rendering.Utilities
+{
+ public class TileBrushCalculator
+ {
+ private readonly Size _imageSize;
+ private readonly Rect _drawRect;
+
+ public bool IsValid { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The brush to be rendered.
+ /// The size of the content of the tile brush.
+ /// The size of the control to which the brush is being rendered.
+ public TileBrushCalculator(TileBrush brush, Size contentSize, Size targetSize)
+ : this(
+ brush.TileMode,
+ brush.Stretch,
+ brush.AlignmentX,
+ brush.AlignmentY,
+ brush.SourceRect,
+ brush.DestinationRect,
+ contentSize,
+ targetSize)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The brush's tile mode.
+ /// The brush's stretch.
+ /// The brush's horizontal alignment.
+ /// The brush's vertical alignment.
+ /// The brush's source rect
+ /// The brush's destination rect.
+ /// The size of the content of the tile brush.
+ /// The size of the control to which the brush is being rendered.
+ public TileBrushCalculator(
+ TileMode tileMode,
+ Stretch stretch,
+ AlignmentX alignmentX,
+ AlignmentY alignmentY,
+ RelativeRect sourceRect,
+ RelativeRect destinationRect,
+ Size contentSize,
+ Size targetSize)
+ {
+ _imageSize = contentSize;
+
+ SourceRect = sourceRect.ToPixels(_imageSize);
+ DestinationRect = destinationRect.ToPixels(targetSize);
+
+ var scale = stretch.CalculateScaling(DestinationRect.Size, SourceRect.Size);
+ var translate = CalculateTranslate(alignmentX, alignmentY, SourceRect, DestinationRect, scale);
+
+ IntermediateSize = tileMode == TileMode.None ? targetSize : DestinationRect.Size;
+ IntermediateTransform = CalculateIntermediateTransform(
+ tileMode,
+ SourceRect,
+ DestinationRect,
+ scale,
+ translate,
+ out _drawRect);
+ }
+
+ ///
+ /// Gets the rectangle on the destination control to which content should be rendered.
+ ///
+ ///
+ /// If of the brush is repeating then this is describes rectangle
+ /// of a single repeat of the tiled content.
+ ///
+ public Rect DestinationRect { get; }
+
+ ///
+ /// Gets the clip rectangle on the intermediate image with which the brush content should be
+ /// drawn when is true.
+ ///
+ public Rect IntermediateClip => _drawRect;
+
+ ///
+ /// Gets the size of the intermediate image that should be created when
+ /// is true.
+ ///
+ public Size IntermediateSize { get; }
+
+ ///
+ /// Gets the transform to be used when rendering to the intermediate image when
+ /// is true.
+ ///
+ public Matrix IntermediateTransform { get; }
+
+ ///
+ /// Gets a value indicating whether an intermediate image should be created in order to
+ /// render the tile brush.
+ ///
+ ///
+ /// Intermediate images are required when a brush's is not repeating
+ /// but the source and destination aspect ratios are unequal, as all of the currently
+ /// supported rendering backends do not support non-tiled image brushes.
+ ///
+ public bool NeedsIntermediate
+ {
+ get
+ {
+ if (IntermediateTransform != Matrix.Identity)
+ return true;
+ if (SourceRect.Position != default(Point))
+ return true;
+ if (SourceRect.Size.AspectRatio == _imageSize.AspectRatio)
+ return false;
+ if ((int)SourceRect.Width != _imageSize.Width ||
+ (int)SourceRect.Height != _imageSize.Height)
+ return true;
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the area of the source content to be rendered.
+ ///
+ public Rect SourceRect { get; }
+
+ public static Vector CalculateTranslate(
+ AlignmentX alignmentX,
+ AlignmentY alignmentY,
+ Rect sourceRect,
+ Rect destinationRect,
+ Vector scale)
+ {
+ var x = 0.0;
+ var y = 0.0;
+ var size = sourceRect.Size * scale;
+
+ switch (alignmentX)
+ {
+ case AlignmentX.Center:
+ x += (destinationRect.Width - size.Width) / 2;
+ break;
+ case AlignmentX.Right:
+ x += destinationRect.Width - size.Width;
+ break;
+ }
+
+ switch (alignmentY)
+ {
+ case AlignmentY.Center:
+ y += (destinationRect.Height - size.Height) / 2;
+ break;
+ case AlignmentY.Bottom:
+ y += destinationRect.Height - size.Height;
+ break;
+ }
+
+ return new Vector(x, y);
+ }
+
+ public static Matrix CalculateIntermediateTransform(
+ TileMode tileMode,
+ Rect sourceRect,
+ Rect destinationRect,
+ Vector scale,
+ Vector translate,
+ out Rect drawRect)
+ {
+ var transform = Matrix.CreateTranslation(-sourceRect.Position) *
+ Matrix.CreateScale(scale) *
+ Matrix.CreateTranslation(translate);
+ Rect dr;
+
+ if (tileMode == TileMode.None)
+ {
+ dr = destinationRect;
+ transform *= Matrix.CreateTranslation(destinationRect.Position);
+ }
+ else
+ {
+ dr = new Rect(destinationRect.Size);
+ }
+
+ drawRect = dr;
+
+ return transform;
+ }
+ }
+}
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/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
index 5cb2c4d494..afe9969f0e 100644
--- a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
+++ b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
@@ -317,10 +317,6 @@ namespace Avalonia.Cairo.Media
{
impl = new ImageBrushImpl(imageBrush, destinationSize);
}
- else if (visualBrush != null)
- {
- impl = new VisualBrushImpl(visualBrush, destinationSize);
- }
else
{
impl = new SolidColorBrushImpl(null, opacityOverride);
diff --git a/src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs b/src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs
index e5aa022913..ee80b9fdf3 100644
--- a/src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs
+++ b/src/Gtk/Avalonia.Cairo/Media/TileBrushes.cs
@@ -16,40 +16,40 @@ namespace Avalonia.Cairo.Media
{
public static SurfacePattern CreateTileBrush(TileBrush brush, Size targetSize)
{
- var helper = new TileBrushImplHelper(brush, targetSize);
- if (!helper.IsValid)
- return null;
+ throw new NotImplementedException();
+ //// var helper = new TileBrushImplHelper(brush, targetSize);
+ //// if (!helper.IsValid)
+ //// return null;
- using (var intermediate = new ImageSurface(Format.ARGB32, (int)helper.IntermediateSize.Width, (int)helper.IntermediateSize.Height))
- using (var ctx = new RenderTarget(intermediate).CreateDrawingContext())
- {
- helper.DrawIntermediate(new Avalonia.Media.DrawingContext(ctx));
+ ////using (var intermediate = new ImageSurface(Format.ARGB32, (int)helper.IntermediateSize.Width, (int)helper.IntermediateSize.Height))
+ //// using (var ctx = new RenderTarget(intermediate).CreateDrawingContext())
+ //// {
+ //// helper.DrawIntermediate(new Avalonia.Media.DrawingContext(ctx));
- var result = new SurfacePattern(intermediate);
+ //// var result = new SurfacePattern(intermediate);
- if ((brush.TileMode & TileMode.FlipXY) != 0)
- {
- // TODO: Currently always FlipXY as that's all cairo supports natively.
- // Support separate FlipX and FlipY by drawing flipped images to intermediate
- // surface.
- result.Extend = Extend.Reflect;
- }
- else
- {
- result.Extend = Extend.Repeat;
- }
+ //// if ((brush.TileMode & TileMode.FlipXY) != 0)
+ //// {
+ //// // TODO: Currently always FlipXY as that's all cairo supports natively.
+ //// // Support separate FlipX and FlipY by drawing flipped images to intermediate
+ //// // surface.
+ //// result.Extend = Extend.Reflect;
+ //// }
+ //// else
+ //// {
+ //// result.Extend = Extend.Repeat;
+ //// }
- if (brush.TileMode != TileMode.None)
- {
- var matrix = result.Matrix;
- matrix.InitTranslate(-helper.DestinationRect.X, -helper.DestinationRect.Y);
- result.Matrix = matrix;
- }
+ //// if (brush.TileMode != TileMode.None)
+ //// {
+ //// var matrix = result.Matrix;
+ //// matrix.InitTranslate(-helper.DestinationRect.X, -helper.DestinationRect.Y);
+ //// result.Matrix = matrix;
+ //// }
- return result;
- }
+ //// return result;
+ //// }
}
-
-
+
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
index 877c4290a4..7f286f4f88 100644
--- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
+++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
@@ -88,7 +88,7 @@
-
+
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index fa937ccb2f..2c841bedb5 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
@@ -332,11 +332,11 @@ namespace Avalonia.Direct2D1.Media
}
else if (imageBrush != null)
{
- return new TileBrushImpl(imageBrush, _renderTarget, destinationSize);
- }
- else if (visualBrush != null)
- {
- return new TileBrushImpl(visualBrush, _renderTarget, destinationSize);
+ return new ImageBrushImpl(
+ imageBrush,
+ _renderTarget,
+ (BitmapImpl)imageBrush.Source.PlatformImpl,
+ destinationSize);
}
else
{
diff --git a/src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
similarity index 51%
rename from src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs
rename to src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
index e567018264..90fe5675ce 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
@@ -3,50 +3,46 @@
using System;
using Avalonia.Media;
-using Avalonia.RenderHelpers;
+using Avalonia.Rendering.Utilities;
using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media
{
- public sealed class TileBrushImpl : BrushImpl
+ public sealed class ImageBrushImpl : BrushImpl
{
- public TileBrushImpl(
+ public ImageBrushImpl(
TileBrush brush,
SharpDX.Direct2D1.RenderTarget target,
+ BitmapImpl bitmap,
Size targetSize)
{
- var helper = new TileBrushImplHelper(brush, targetSize);
- if (!helper.IsValid)
- return;
+ var calc = new TileBrushCalculator(brush, new Size(bitmap.PixelWidth, bitmap.PixelHeight), targetSize);
- using (var intermediate = new BitmapRenderTarget(target, CompatibleRenderTargetOptions.None, helper.IntermediateSize.ToSharpDX()))
+ if (!calc.NeedsIntermediate)
{
- using (var ctx = new RenderTarget(intermediate).CreateDrawingContext())
- {
- intermediate.Clear(null);
- helper.DrawIntermediate(new DrawingContext(ctx));
- }
-
PlatformBrush = new BitmapBrush(
target,
- intermediate.Bitmap,
+ bitmap.GetDirect2DBitmap(target),
GetBitmapBrushProperties(brush),
- GetBrushProperties(brush, helper.DestinationRect));
+ GetBrushProperties(brush, calc.DestinationRect));
+ }
+ else
+ {
+ using (var intermediate = RenderIntermediate(target, bitmap, calc))
+ {
+ PlatformBrush = new BitmapBrush(
+ target,
+ intermediate.Bitmap,
+ GetBitmapBrushProperties(brush),
+ GetBrushProperties(brush, calc.DestinationRect));
+ }
}
}
- private static BrushProperties GetBrushProperties(TileBrush brush, Rect destinationRect)
+ public override void Dispose()
{
- var tileTransform =
- brush.TileMode != TileMode.None ?
- Matrix.CreateTranslation(destinationRect.X, destinationRect.Y) :
- Matrix.Identity;
-
- return new BrushProperties
- {
- Opacity = (float)brush.Opacity,
- Transform = tileTransform.ToDirect2D(),
- };
+ ((BitmapBrush)PlatformBrush)?.Bitmap.Dispose();
+ base.Dispose();
}
private static BitmapBrushProperties GetBitmapBrushProperties(TileBrush brush)
@@ -60,6 +56,20 @@ namespace Avalonia.Direct2D1.Media
};
}
+ private static BrushProperties GetBrushProperties(TileBrush brush, Rect destinationRect)
+ {
+ var tileTransform =
+ brush.TileMode != TileMode.None ?
+ Matrix.CreateTranslation(destinationRect.X, destinationRect.Y) :
+ Matrix.Identity;
+
+ return new BrushProperties
+ {
+ Opacity = (float)brush.Opacity,
+ Transform = tileTransform.ToDirect2D(),
+ };
+ }
+
private static ExtendMode GetExtendModeX(TileMode tileMode)
{
return (tileMode & TileMode.FlipX) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap;
@@ -70,10 +80,28 @@ namespace Avalonia.Direct2D1.Media
return (tileMode & TileMode.FlipY) != 0 ? ExtendMode.Mirror : ExtendMode.Wrap;
}
- public override void Dispose()
+ private BitmapRenderTarget RenderIntermediate(
+ SharpDX.Direct2D1.RenderTarget target,
+ BitmapImpl bitmap,
+ TileBrushCalculator calc)
{
- ((BitmapBrush)PlatformBrush)?.Bitmap.Dispose();
- base.Dispose();
+ var result = new BitmapRenderTarget(
+ target,
+ CompatibleRenderTargetOptions.None,
+ calc.IntermediateSize.ToSharpDX());
+
+ using (var context = new RenderTarget(result).CreateDrawingContext())
+ {
+ var rect = new Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
+
+ context.Clear(Colors.Transparent);
+ context.PushClip(calc.IntermediateClip);
+ context.Transform = calc.IntermediateTransform;
+ context.DrawImage(bitmap, 1, rect, rect);
+ context.PopClip();
+ }
+
+ return result;
}
}
}
diff --git a/tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v2.ncrunchproject b/tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v2.ncrunchproject
index 6e89ef281e..ad599eafcf 100644
--- a/tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v2.ncrunchproject
+++ b/tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v2.ncrunchproject
@@ -23,5 +23,70 @@
AutoDetect
STA
x86
+
+
+ Avalonia.Cairo.RenderTests.Controls.BorderTests
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_Fill_NoTile
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_FlipXY_TopLeftDest
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_BottomRight
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_Center
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterDest
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterSource
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_BottomRightQuarterSource_BottomRightQuarterDest
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_Tile_BottomRightQuarterSource_CenterQuarterDest
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_Uniform_NoTile
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_UniformToFill_NoTile
+
+
+ Avalonia.Cairo.RenderTests.Controls.ImageTests
+
+
+ Avalonia.Cairo.RenderTests.GeometryClippingTests
+
+
+ Avalonia.Cairo.RenderTests.Media.LinearGradientBrushTests
+
+
+ Avalonia.Cairo.RenderTests.Media.VisualBrushTests
+
+
+ Avalonia.Cairo.RenderTests.OpacityMaskTests
+
+
+ Avalonia.Cairo.RenderTests.Shapes.EllipseTests
+
+
+ Avalonia.Cairo.RenderTests.Shapes.LineTests
+
+
+ Avalonia.Cairo.RenderTests.Shapes.PathTests
+
+
+ Avalonia.Cairo.RenderTests.Shapes.RectangleTests
+
+
+ Avalonia.Cairo.RenderTests.Media.ImageBrushTests.ImageBrush_NoStretch_NoTile_Alignment_TopLeft
+
+
AbnormalReferenceResolution
\ No newline at end of file
diff --git a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj
index c7a8739404..51a33f8f29 100644
--- a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj
+++ b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj
@@ -88,6 +88,7 @@
+
diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs
new file mode 100644
index 0000000000..8d70b45842
--- /dev/null
+++ b/tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs
@@ -0,0 +1,139 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Rendering.Utilities;
+using Xunit;
+
+namespace Avalonia.Visuals.UnitTests.Rendering.Utilities
+{
+ public class TileBrushCalculatorTests
+ {
+ [Fact]
+ public void NoTile_Fill_1x()
+ {
+ var result = new TileBrushCalculator(
+ TileMode.None,
+ Stretch.Fill,
+ AlignmentX.Center,
+ AlignmentY.Center,
+ RelativeRect.Fill,
+ RelativeRect.Fill,
+ new Size(100, 100),
+ new Size(100, 100));
+
+ Assert.False(result.NeedsIntermediate);
+ Assert.Equal(new Rect(0, 0, 100, 100), result.SourceRect);
+ Assert.Equal(new Rect(0, 0, 100, 100), result.DestinationRect);
+ }
+
+ [Fact]
+ public void NoTile_Fill_2x()
+ {
+ var result = new TileBrushCalculator(
+ TileMode.None,
+ Stretch.Fill,
+ AlignmentX.Center,
+ AlignmentY.Center,
+ RelativeRect.Fill,
+ RelativeRect.Fill,
+ new Size(100, 100),
+ new Size(200, 200));
+
+ // TODO: This doesn't need an intermediate render target.
+ Assert.True(result.NeedsIntermediate);
+ Assert.Equal(new Rect(0, 0, 100, 100), result.SourceRect);
+ Assert.Equal(new Rect(0, 0, 200, 200), result.DestinationRect);
+ }
+
+ [Fact]
+ public void NoTile_Uniform_CenterHoriz()
+ {
+ var result = new TileBrushCalculator(
+ TileMode.None,
+ Stretch.Uniform,
+ AlignmentX.Center,
+ AlignmentY.Center,
+ RelativeRect.Fill,
+ RelativeRect.Fill,
+ new Size(100, 100),
+ new Size(200, 100));
+
+ Assert.True(result.NeedsIntermediate);
+ Assert.Equal(new Rect(0, 0, 100, 100), result.SourceRect);
+ Assert.Equal(new Rect(0, 0, 200, 100), result.DestinationRect);
+ Assert.Equal(new Size(200, 100), result.IntermediateSize);
+ Assert.Equal(Matrix.CreateTranslation(50, 0), result.IntermediateTransform);
+ }
+
+ [Fact]
+ public void NoTile_Uniform_CenterVert()
+ {
+ var result = new TileBrushCalculator(
+ TileMode.None,
+ Stretch.Uniform,
+ AlignmentX.Center,
+ AlignmentY.Center,
+ RelativeRect.Fill,
+ RelativeRect.Fill,
+ new Size(100, 100),
+ new Size(100, 200));
+
+ Assert.True(result.NeedsIntermediate);
+ Assert.Equal(new Rect(0, 0, 100, 100), result.SourceRect);
+ Assert.Equal(new Rect(0, 0, 100, 200), result.DestinationRect);
+ Assert.Equal(new Size(100, 200), result.IntermediateSize);
+ Assert.Equal(Matrix.CreateTranslation(0, 50), result.IntermediateTransform);
+ }
+
+ [Fact]
+ public void NoTile_NoStretch_BottomRightQuarterDest()
+ {
+ var result = new TileBrushCalculator(
+ TileMode.None,
+ Stretch.None,
+ AlignmentX.Center,
+ AlignmentY.Center,
+ RelativeRect.Fill,
+ new RelativeRect(0.5, 0.5, 0.5, 0.5, RelativeUnit.Relative),
+ new Size(800, 800),
+ new Size(400, 400));
+
+ Assert.True(result.NeedsIntermediate);
+ Assert.Equal(new Rect(0, 0, 800, 800), result.SourceRect);
+ Assert.Equal(new Rect(200, 200, 200, 200), result.DestinationRect);
+ Assert.Equal(new Size(400, 400), result.IntermediateSize);
+ Assert.Equal(new Rect(200, 200, 200, 200), result.IntermediateClip);
+ Assert.Equal(Matrix.CreateTranslation(-100, -100), result.IntermediateTransform);
+ }
+
+ [Fact]
+ public void Tile_NoStretch_BottomRightQuarterSource_CenterQuarterDest()
+ {
+ var result = new TileBrushCalculator(
+ TileMode.Tile,
+ Stretch.None,
+ AlignmentX.Center,
+ AlignmentY.Center,
+ new RelativeRect(0.5, 0.5, 0.5, 0.5, RelativeUnit.Relative),
+ new RelativeRect(0.25, 0.25, 0.5, 0.5, RelativeUnit.Relative),
+ new Size(800, 800),
+ new Size(400, 400));
+
+ var b = new VisualBrush
+ {
+ TileMode = TileMode.Tile,
+ Stretch = Stretch.None,
+ SourceRect = new RelativeRect(0.5, 0.5, 0.5, 0.5, RelativeUnit.Relative),
+ DestinationRect = new RelativeRect(0.25, 0.25, 0.5, 0.5, RelativeUnit.Relative),
+ Visual = new Border { Width = 400, Height = 400 },
+ };
+
+ Assert.True(result.NeedsIntermediate);
+ Assert.Equal(new Rect(400, 400, 400, 400), result.SourceRect);
+ Assert.Equal(new Rect(100, 100, 200, 200), result.DestinationRect);
+ Assert.Equal(new Size(200, 200), result.IntermediateSize);
+ Assert.Equal(new Rect(0, 0, 200, 200), result.IntermediateClip);
+ Assert.Equal(Matrix.CreateTranslation(-500, -500), result.IntermediateTransform);
+ }
+ }
+}