diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props
index b9075b957b..8fc91aca14 100644
--- a/samples/Directory.Build.props
+++ b/samples/Directory.Build.props
@@ -1,6 +1,7 @@
false
+ $(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netcoreapp2.0\Avalonia.Designer.HostApp.dll
diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
index 50aa8a9e71..f75184c686 100644
--- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
@@ -365,12 +365,8 @@ namespace Avalonia.Controls.Presenters
if (useLayoutRounding)
{
- sizeForChild = new Size(
- Math.Ceiling(sizeForChild.Width * scale) / scale,
- Math.Ceiling(sizeForChild.Height * scale) / scale);
- availableSize = new Size(
- Math.Ceiling(availableSize.Width * scale) / scale,
- Math.Ceiling(availableSize.Height * scale) / scale);
+ sizeForChild = LayoutHelper.RoundLayoutSize(sizeForChild, scale, scale);
+ availableSize = LayoutHelper.RoundLayoutSize(availableSize, scale, scale);
}
switch (horizontalContentAlignment)
@@ -395,8 +391,8 @@ namespace Avalonia.Controls.Presenters
if (useLayoutRounding)
{
- originX = Math.Floor(originX * scale) / scale;
- originY = Math.Floor(originY * scale) / scale;
+ originX = LayoutHelper.RoundLayoutValue(originX, scale);
+ originY = LayoutHelper.RoundLayoutValue(originY, scale);
}
var boundsForChild =
diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs
index 2b61de00a7..d8fa00deb7 100644
--- a/src/Avalonia.Layout/LayoutHelper.cs
+++ b/src/Avalonia.Layout/LayoutHelper.cs
@@ -82,6 +82,64 @@ namespace Avalonia.Layout
InnerInvalidateMeasure(control);
}
+ ///
+ /// Rounds a size to integer values for layout purposes, compensating for high DPI screen
+ /// coordinates.
+ ///
+ /// Input size.
+ /// DPI along x-dimension.
+ /// DPI along y-dimension.
+ /// Value of size that will be rounded under screen DPI.
+ ///
+ /// This is a layout helper method. It takes DPI into account and also does not return
+ /// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper
+ /// associated with the UseLayoutRounding property and should not be used as a general rounding
+ /// utility.
+ ///
+ public static Size RoundLayoutSize(Size size, double dpiScaleX, double dpiScaleY)
+ {
+ return new Size(RoundLayoutValue(size.Width, dpiScaleX), RoundLayoutValue(size.Height, dpiScaleY));
+ }
+
+ ///
+ /// Calculates the value to be used for layout rounding at high DPI.
+ ///
+ /// Input value to be rounded.
+ /// Ratio of screen's DPI to layout DPI
+ /// Adjusted value that will produce layout rounding on screen at high dpi.
+ ///
+ /// This is a layout helper method. It takes DPI into account and also does not return
+ /// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper
+ /// associated with the UseLayoutRounding property and should not be used as a general rounding
+ /// utility.
+ ///
+ public static double RoundLayoutValue(double value, double dpiScale)
+ {
+ double newValue;
+
+ // If DPI == 1, don't use DPI-aware rounding.
+ if (!MathUtilities.AreClose(dpiScale, 1.0))
+ {
+ newValue = Math.Round(value * dpiScale) / dpiScale;
+
+ // If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue),
+ // use the original value.
+ if (double.IsNaN(newValue) ||
+ double.IsInfinity(newValue) ||
+ MathUtilities.AreClose(newValue, double.MaxValue))
+ {
+ newValue = value;
+ }
+ }
+ else
+ {
+ newValue = Math.Round(value);
+ }
+
+ return newValue;
+ }
+
+
///
/// Calculates the min and max height for a control. Ported from WPF.
///
diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs
index cbeab0482e..ce5200f4a4 100644
--- a/src/Avalonia.Layout/Layoutable.cs
+++ b/src/Avalonia.Layout/Layoutable.cs
@@ -1,5 +1,6 @@
using System;
using Avalonia.Logging;
+using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Layout
@@ -545,8 +546,8 @@ namespace Avalonia.Layout
if (UseLayoutRounding)
{
var scale = GetLayoutScale();
- width = Math.Ceiling(width * scale) / scale;
- height = Math.Ceiling(height * scale) / scale;
+ width = LayoutHelper.RoundLayoutValue(width, scale);
+ height = LayoutHelper.RoundLayoutValue(height, scale);
}
return NonNegative(new Size(width, height).Inflate(margin));
@@ -623,12 +624,8 @@ namespace Avalonia.Layout
if (useLayoutRounding)
{
- size = new Size(
- Math.Ceiling(size.Width * scale) / scale,
- Math.Ceiling(size.Height * scale) / scale);
- availableSizeMinusMargins = new Size(
- Math.Ceiling(availableSizeMinusMargins.Width * scale) / scale,
- Math.Ceiling(availableSizeMinusMargins.Height * scale) / scale);
+ size = LayoutHelper.RoundLayoutSize(size, scale, scale);
+ availableSizeMinusMargins = LayoutHelper.RoundLayoutSize(availableSizeMinusMargins, scale, scale);
}
size = ArrangeOverride(size).Constrain(size);
@@ -657,8 +654,8 @@ namespace Avalonia.Layout
if (useLayoutRounding)
{
- originX = Math.Floor(originX * scale) / scale;
- originY = Math.Floor(originY * scale) / scale;
+ originX = LayoutHelper.RoundLayoutValue(originX, scale);
+ originY = LayoutHelper.RoundLayoutValue(originY, scale);
}
Bounds = new Rect(originX, originY, size.Width, size.Height);
diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/ScrollBar.xaml
index 64a4399d16..0f8fa4986d 100644
--- a/src/Avalonia.Themes.Default/ScrollBar.xaml
+++ b/src/Avalonia.Themes.Default/ScrollBar.xaml
@@ -3,7 +3,8 @@
-
+
-
+
x.InvalidateMeasure(root), Times.Once());
}
+ [Theory]
+ [InlineData(16, 6, 5.333333333333333)]
+ [InlineData(18, 10, 4)]
+ public void UseLayoutRounding_Arranges_Center_Alignment_Correctly_With_Fractional_Scaling(
+ double containerWidth,
+ double childWidth,
+ double expectedX)
+ {
+ Border target;
+ var root = new TestRoot
+ {
+ LayoutScaling = 1.5,
+ UseLayoutRounding = true,
+ Child = new Decorator
+ {
+ Width = containerWidth,
+ Height = 100,
+ Child = target = new Border
+ {
+ Width = childWidth,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ }
+ }
+ };
+
+ root.Measure(new Size(100, 100));
+ root.Arrange(new Rect(target.DesiredSize));
+
+ Assert.Equal(new Rect(expectedX, 0, childWidth, 100), target.Bounds);
+ }
+
private class TestLayoutable : Layoutable
{
public Size ArrangeSize { get; private set; }
diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs
index 56a1a6c245..f291d386aa 100644
--- a/tests/Avalonia.UnitTests/TestRoot.cs
+++ b/tests/Avalonia.UnitTests/TestRoot.cs
@@ -42,7 +42,7 @@ namespace Avalonia.UnitTests
public Size MaxClientSize { get; set; } = Size.Infinity;
- public double LayoutScaling => 1;
+ public double LayoutScaling { get; set; } = 1;
public ILayoutManager LayoutManager { get; set; } = new LayoutManager();
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png
index 3996782889..ea81296017 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_InTree_Visual.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_InTree_Visual.expected.png
index f93814556e..a8ec09df99 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_InTree_Visual.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_InTree_Visual.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipXY_TopLeftDest.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipXY_TopLeftDest.expected.png
index 84b76e795e..485b03acd3 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipXY_TopLeftDest.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipXY_TopLeftDest.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipX_TopLeftDest.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipX_TopLeftDest.expected.png
index f2346a64a0..a6bc0daee3 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipX_TopLeftDest.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipX_TopLeftDest.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipY_TopLeftDest.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipY_TopLeftDest.expected.png
index cb355b9ce0..8c622c37b5 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipY_TopLeftDest.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_FlipY_TopLeftDest.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_NoTile_Alignment_Center.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_NoTile_Alignment_Center.expected.png
index 08f41b3ef5..bfe5fd7fbb 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_NoTile_Alignment_Center.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_NoTile_Alignment_Center.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png
index a0b6afeeeb..f3c3c4aee2 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png
index 1d620ae100..80116d81c6 100644
Binary files a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png differ
diff --git a/tests/TestFiles/Direct2D1/Shapes/Path/Path_Expander_With_Border.expected.png b/tests/TestFiles/Direct2D1/Shapes/Path/Path_Expander_With_Border.expected.png
index 4a7f4c26b0..7a7d70a521 100644
Binary files a/tests/TestFiles/Direct2D1/Shapes/Path/Path_Expander_With_Border.expected.png and b/tests/TestFiles/Direct2D1/Shapes/Path/Path_Expander_With_Border.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png
index 078bca57bc..474e2bb2bf 100644
Binary files a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Fill_NoTile.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_InTree_Visual.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_InTree_Visual.expected.png
index f93814556e..4ca25db112 100644
Binary files a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_InTree_Visual.expected.png and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_InTree_Visual.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipXY_TopLeftDest.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipXY_TopLeftDest.expected.png
index 84b76e795e..3c9c88e8f1 100644
Binary files a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipXY_TopLeftDest.expected.png and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipXY_TopLeftDest.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipX_TopLeftDest.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipX_TopLeftDest.expected.png
index f2346a64a0..e9efe1e796 100644
Binary files a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipX_TopLeftDest.expected.png and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipX_TopLeftDest.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipY_TopLeftDest.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipY_TopLeftDest.expected.png
index cb355b9ce0..b864ffca33 100644
Binary files a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipY_TopLeftDest.expected.png and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_FlipY_TopLeftDest.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_NoTile_Alignment_Center.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_NoTile_Alignment_Center.expected.png
index 08f41b3ef5..fa359eac11 100644
Binary files a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_NoTile_Alignment_Center.expected.png and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_NoTile_Alignment_Center.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png
index a0b6afeeeb..778e3f6c13 100644
Binary files a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_NoStretch_NoTile_BottomRightQuarterDest.expected.png differ
diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png
index 1f1ac05853..d7ef920967 100644
Binary files a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_UniformToFill_NoTile.expected.png differ
diff --git a/tests/TestFiles/Skia/Shapes/Path/Path_Expander_With_Border.expected.png b/tests/TestFiles/Skia/Shapes/Path/Path_Expander_With_Border.expected.png
index 4a7f4c26b0..7727284050 100644
Binary files a/tests/TestFiles/Skia/Shapes/Path/Path_Expander_With_Border.expected.png and b/tests/TestFiles/Skia/Shapes/Path/Path_Expander_With_Border.expected.png differ