Browse Source

Merge pull request #8882 from AvaloniaUI/fixes/8866-border-child-rounding

Make layout rounding more resilient to floating point errors.
pull/8920/head
Steven Kirk 4 years ago
committed by GitHub
parent
commit
320d074564
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      src/Avalonia.Base/Layout/LayoutHelper.cs
  2. 2
      src/Avalonia.Controls/Utils/BorderRenderHelper.cs
  3. 57
      tests/Avalonia.Controls.UnitTests/BorderTests.cs

11
src/Avalonia.Base/Layout/LayoutHelper.cs

@ -251,6 +251,17 @@ namespace Avalonia.Layout
{
double newValue;
// Round the value to avoid FP errors. This is needed because if `value` has a floating
// point precision error (e.g. 79.333333333333343) then when it's multiplied by
// `dpiScale` and rounded up, it will be rounded up to a value one greater than it
// should be.
#if NET6_0_OR_GREATER
value = Math.Round(value, 8, MidpointRounding.ToZero);
#else
// MidpointRounding.ToZero isn't available in netstandard2.0.
value = Math.Truncate(value * 1e8) / 1e8;
#endif
// If DPI == 1, don't use DPI-aware rounding.
if (!MathUtilities.IsOne(dpiScale))
{

2
src/Avalonia.Controls/Utils/BorderRenderHelper.cs

@ -61,7 +61,7 @@ namespace Avalonia.Controls.Utils
_backgroundGeometryCache = null;
}
if (boundRect.Width != 0 && innerRect.Height != 0)
if (boundRect.Width != 0 && boundRect.Height != 0)
{
var borderGeometryKeypoints =
new BorderGeometryKeypoints(boundRect, borderThickness, cornerRadius, false);

57
tests/Avalonia.Controls.UnitTests/BorderTests.cs

@ -78,7 +78,7 @@ namespace Avalonia.Controls.UnitTests
}
};
var root = CreatedRoot(1.5, target);
var root = CreateRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
@ -101,7 +101,7 @@ namespace Avalonia.Controls.UnitTests
}
};
var root = CreatedRoot(1.5, target);
var root = CreateRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
@ -111,7 +111,58 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Size(104, 104), target.DesiredSize);
}
private static TestRoot CreatedRoot(
[Fact]
public void Measure_Arranges_Child_To_Rounded_BorderThickness()
{
Canvas child;
var target = new Border
{
BorderThickness = new Thickness(1),
Width = 82,
Height = 82,
Child = child = new Canvas(),
};
var root = CreateRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 1 pixel border thickness is rounded up to 1.3333; for both sides it is 2.6666
// - Size of 82 needs no rounding
// - Minus border thickness, space for child is 82 - 2.6666 = 79.3333
Assert.Equal(1.3333, child.Bounds.Left, 3);
Assert.Equal(1.3333, child.Bounds.Top, 3);
Assert.Equal(79.3333, child.Bounds.Width, 3);
Assert.Equal(79.3333, child.Bounds.Height, 3);
}
[Fact]
public void Measure_Arranges_Child_With_Rounded_Margin()
{
Border child;
var target = new Border
{
Width = 220,
Height = 220,
Child = child = new Border
{
Margin = new Thickness(0, 25, 25, 25),
},
};
var root = CreateRoot(1.5, target);
root.LayoutManager.ExecuteInitialLayoutPass();
// - 25 margin gets rounded up to 25.3333
// - Size of 220 needs no rounding
Assert.Equal(0, child.Bounds.Left, 3);
Assert.Equal(25.3333, child.Bounds.Top, 3);
Assert.Equal(194.6666, child.Bounds.Width, 3);
Assert.Equal(169.3333, child.Bounds.Height, 3);
}
private static TestRoot CreateRoot(
double scaling,
Control child,
Size? constraint = null)

Loading…
Cancel
Save