From 1ced34f15fcfc2a1a242ab6d196e840e26774a0b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 18 Nov 2018 16:05:26 +0000 Subject: [PATCH 1/7] fix tilebrush scaling for direct2d. --- .../Rendering/Utilities/TileBrushCalculator.cs | 14 +++++++++----- .../Avalonia.Direct2D1/Media/ImageBrushImpl.cs | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs index 52a8fb5ab7..97af33c3e2 100644 --- a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs +++ b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs @@ -9,6 +9,7 @@ namespace Avalonia.Rendering.Utilities { private readonly Size _imageSize; private readonly Rect _drawRect; + private readonly Vector _dpi; public bool IsValid { get; } @@ -18,7 +19,7 @@ namespace Avalonia.Rendering.Utilities /// 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(ITileBrush brush, Size contentSize, Size targetSize) + public TileBrushCalculator(ITileBrush brush, Size contentSize, Size targetSize, Vector dpi) : this( brush.TileMode, brush.Stretch, @@ -27,7 +28,8 @@ namespace Avalonia.Rendering.Utilities brush.SourceRect, brush.DestinationRect, contentSize, - targetSize) + targetSize, + dpi) { } @@ -50,12 +52,14 @@ namespace Avalonia.Rendering.Utilities RelativeRect sourceRect, RelativeRect destinationRect, Size contentSize, - Size targetSize) + Size targetSize, + Vector dpi) { _imageSize = contentSize; + _dpi = dpi; - SourceRect = sourceRect.ToPixels(_imageSize); - DestinationRect = destinationRect.ToPixels(targetSize); + SourceRect = sourceRect.ToPixels(_imageSize) * (_dpi / 96); + DestinationRect = destinationRect.ToPixels(targetSize) * (dpi / 96); var scale = stretch.CalculateScaling(DestinationRect.Size, SourceRect.Size); var translate = CalculateTranslate(alignmentX, alignmentY, SourceRect, DestinationRect, scale); diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 55877cefcb..6c2a88af17 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -20,8 +20,8 @@ namespace Avalonia.Direct2D1.Media BitmapImpl bitmap, Size targetSize) { - var calc = new TileBrushCalculator(brush, bitmap.PixelSize.ToSize(96), targetSize); - + var calc = new TileBrushCalculator(brush, bitmap.PixelSize.ToSize(96), targetSize, new Vector(target.DotsPerInch.Width, target.DotsPerInch.Height)); + if (!calc.NeedsIntermediate) { _bitmap = bitmap.GetDirect2DBitmap(target); From f1c2c72400ee18d10f3e796ae788fce9a06efb9a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 24 Nov 2018 15:50:47 +0100 Subject: [PATCH 2/7] Added failing tests for #1614. --- .../Media/VisualBrushTests.cs | 123 ++++++++++++++++++ tests/Avalonia.RenderTests/TestBase.cs | 8 +- ...sualBrush_Checkerboard_96_Dpi.expected.png | Bin 0 -> 765 bytes ...sualBrush_Checkerboard_96_Dpi.expected.png | Bin 0 -> 765 bytes 4 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Checkerboard_96_Dpi.expected.png create mode 100644 tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Checkerboard_96_Dpi.expected.png diff --git a/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs b/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs index 6f90dfd19e..aee83d5e0b 100644 --- a/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs @@ -449,5 +449,128 @@ namespace Avalonia.Direct2D1.RenderTests.Media await RenderToFile(target); CompareImages(); } + + [Fact] + public async Task VisualBrush_Checkerboard_96_Dpi() + { + var target = new Border + { + Width = 200, + Height = 200, + Background = new VisualBrush + { + DestinationRect = new RelativeRect(0, 0, 16, 16, RelativeUnit.Absolute), + TileMode = TileMode.Tile, + Visual = new Canvas + { + Width = 16, + Height= 16, + Background = Brushes.Red, + Children = + { + new Rectangle + { + Width = 8, + Height = 8, + Fill = Brushes.Green, + }, + new Rectangle + { + Width = 8, + Height = 8, + Fill = Brushes.Green, + [Canvas.LeftProperty] = 8, + [Canvas.TopProperty] = 8, + }, + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task VisualBrush_Checkerboard_144_Dpi() + { + var target = new Border + { + Width = 200, + Height = 200, + Background = new VisualBrush + { + DestinationRect = new RelativeRect(0, 0, 16, 16, RelativeUnit.Absolute), + TileMode = TileMode.Tile, + Visual = new Canvas + { + Width = 16, + Height = 16, + Background = Brushes.Red, + Children = + { + new Rectangle + { + Width = 8, + Height = 8, + Fill = Brushes.Green, + }, + new Rectangle + { + Width = 8, + Height = 8, + Fill = Brushes.Green, + [Canvas.LeftProperty] = 8, + [Canvas.TopProperty] = 8, + }, + } + } + } + }; + + await RenderToFile(target, dpi: 144); + CompareImages(); + } + + [Fact] + public async Task VisualBrush_Checkerboard_192_Dpi() + { + var target = new Border + { + Width = 200, + Height = 200, + Background = new VisualBrush + { + DestinationRect = new RelativeRect(0, 0, 16, 16, RelativeUnit.Absolute), + TileMode = TileMode.Tile, + Visual = new Canvas + { + Width = 16, + Height = 16, + Background = Brushes.Red, + Children = + { + new Rectangle + { + Width = 8, + Height = 8, + Fill = Brushes.Green, + }, + new Rectangle + { + Width = 8, + Height = 8, + Fill = Brushes.Green, + [Canvas.LeftProperty] = 8, + [Canvas.TopProperty] = 8, + }, + } + } + } + }; + + await RenderToFile(target, dpi: 192); + CompareImages(); + } } } diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 2892615e48..4c8abd85f7 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -63,7 +63,7 @@ namespace Avalonia.Direct2D1.RenderTests get; } - protected async Task RenderToFile(Control target, [CallerMemberName] string testName = "") + protected async Task RenderToFile(Control target, [CallerMemberName] string testName = "", double dpi = 96) { if (!Directory.Exists(OutputPath)) { @@ -75,9 +75,9 @@ namespace Avalonia.Direct2D1.RenderTests var factory = AvaloniaLocator.Current.GetService(); var pixelSize = new PixelSize((int)target.Width, (int)target.Height); var size = new Size(target.Width, target.Height); - var dpi = new Vector(96, 96); + var dpiVector = new Vector(dpi, dpi); - using (RenderTargetBitmap bitmap = new RenderTargetBitmap(pixelSize, dpi)) + using (RenderTargetBitmap bitmap = new RenderTargetBitmap(pixelSize, dpiVector)) { target.Measure(size); target.Arrange(new Rect(size)); @@ -85,7 +85,7 @@ namespace Avalonia.Direct2D1.RenderTests bitmap.Save(immediatePath); } - using (var rtb = factory.CreateRenderTargetBitmap(pixelSize, dpi)) + using (var rtb = factory.CreateRenderTargetBitmap(pixelSize, dpiVector)) using (var renderer = new DeferredRenderer(target, rtb)) { target.Measure(size); diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Checkerboard_96_Dpi.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Checkerboard_96_Dpi.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..0f88a0d4683ac02fd277cca06d5a7257f1d45ecc GIT binary patch literal 765 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZkrl}2EbxddW?ba4!+h@5toZc^y};8W>2}7 zt7(*N%9e8KEaTIA*PmCd`%rVQzxns{`?vSYvwwSk{9ye1{rBb{;AdfSR1iQA@1DQ< z-~7Y+=kLOV|8+8dzVGEmm1ki(SS|2-%f50)1rC-b2PC3`KWYB8?|K|8jSd3H#Cyj# zmw(-3ZB!88U_v4u*ml%L-QSIDH`o<>*f;vWt`RX)@0(D~Zglt{cf@Y( t`&d-7fmYpXzOnr4sN?A$@)o<+<$wOs@-f%`u@M6hc)I$ztaD0e0suUW=+yuK literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Checkerboard_96_Dpi.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Checkerboard_96_Dpi.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..0f88a0d4683ac02fd277cca06d5a7257f1d45ecc GIT binary patch literal 765 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZkrl}2EbxddW?ba4!+h@5toZc^y};8W>2}7 zt7(*N%9e8KEaTIA*PmCd`%rVQzxns{`?vSYvwwSk{9ye1{rBb{;AdfSR1iQA@1DQ< z-~7Y+=kLOV|8+8dzVGEmm1ki(SS|2-%f50)1rC-b2PC3`KWYB8?|K|8jSd3H#Cyj# zmw(-3ZB!88U_v4u*ml%L-QSIDH`o<>*f;vWt`RX)@0(D~Zglt{cf@Y( t`&d-7fmYpXzOnr4sN?A$@)o<+<$wOs@-f%`u@M6hc)I$ztaD0e0suUW=+yuK literal 0 HcmV?d00001 From eecd3555f97c9a19307c2edec2994ae2483ae6d0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 24 Nov 2018 16:38:41 +0100 Subject: [PATCH 3/7] Fix DPI in ImageBrushImpl. And add expected output for tests. D2D now passes, skia still renders incorrectly. --- .../Rendering/Utilities/TileBrushCalculator.cs | 14 +++++--------- .../Avalonia.Direct2D1/Media/ImageBrushImpl.cs | 5 +++-- .../VisualBrush_Checkerboard_144_Dpi.expected.png | Bin 0 -> 752 bytes .../VisualBrush_Checkerboard_192_Dpi.expected.png | Bin 0 -> 732 bytes .../VisualBrush_Checkerboard_144_Dpi.expected.png | Bin 0 -> 752 bytes .../VisualBrush_Checkerboard_192_Dpi.expected.png | Bin 0 -> 732 bytes 6 files changed, 8 insertions(+), 11 deletions(-) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Checkerboard_144_Dpi.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Checkerboard_192_Dpi.expected.png create mode 100644 tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Checkerboard_144_Dpi.expected.png create mode 100644 tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Checkerboard_192_Dpi.expected.png diff --git a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs index 97af33c3e2..52a8fb5ab7 100644 --- a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs +++ b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs @@ -9,7 +9,6 @@ namespace Avalonia.Rendering.Utilities { private readonly Size _imageSize; private readonly Rect _drawRect; - private readonly Vector _dpi; public bool IsValid { get; } @@ -19,7 +18,7 @@ namespace Avalonia.Rendering.Utilities /// 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(ITileBrush brush, Size contentSize, Size targetSize, Vector dpi) + public TileBrushCalculator(ITileBrush brush, Size contentSize, Size targetSize) : this( brush.TileMode, brush.Stretch, @@ -28,8 +27,7 @@ namespace Avalonia.Rendering.Utilities brush.SourceRect, brush.DestinationRect, contentSize, - targetSize, - dpi) + targetSize) { } @@ -52,14 +50,12 @@ namespace Avalonia.Rendering.Utilities RelativeRect sourceRect, RelativeRect destinationRect, Size contentSize, - Size targetSize, - Vector dpi) + Size targetSize) { _imageSize = contentSize; - _dpi = dpi; - SourceRect = sourceRect.ToPixels(_imageSize) * (_dpi / 96); - DestinationRect = destinationRect.ToPixels(targetSize) * (dpi / 96); + SourceRect = sourceRect.ToPixels(_imageSize); + DestinationRect = destinationRect.ToPixels(targetSize); var scale = stretch.CalculateScaling(DestinationRect.Size, SourceRect.Size); var translate = CalculateTranslate(alignmentX, alignmentY, SourceRect, DestinationRect, scale); diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 6c2a88af17..2ca2a77fb8 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -20,8 +20,9 @@ namespace Avalonia.Direct2D1.Media BitmapImpl bitmap, Size targetSize) { - var calc = new TileBrushCalculator(brush, bitmap.PixelSize.ToSize(96), targetSize, new Vector(target.DotsPerInch.Width, target.DotsPerInch.Height)); - + var dpi = new Vector(target.DotsPerInch.Width, target.DotsPerInch.Height); + var calc = new TileBrushCalculator(brush, bitmap.PixelSize.ToSize(dpi), targetSize); + if (!calc.NeedsIntermediate) { _bitmap = bitmap.GetDirect2DBitmap(target); diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Checkerboard_144_Dpi.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Checkerboard_144_Dpi.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..7e674b849ae4f5763b302d07b57aff122e612679 GIT binary patch literal 752 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZkrl}2EbxddW?&Fg1z|?dAe9dc3{0(_E{-7;ac}P^<}o`8xE{PY=Z^V(0q5tX zVO*Z8yKWr#^=4(K>GjIFzikTk-;1w%S@om(_u0DNum7(9uKuCXL4kv%2@7$kbbk8( zqo3~2OWXfVM%xNb!1@J z*w6hZkrl}2EbxddW?+zQ2Vutkryo-p7?=t@T^vIy;@;j>%sb*B;CisoZ=ZR2N8&fe z?M_jlDry(q{d>cT=bqnr{=nZ#ySQC#|MUOVM%xNb!1@J z*w6hZkrl}2EbxddW?&Fg1z|?dAe9dc3{0(_E{-7;ac}P^<}o`8xE{PY=Z^V(0q5tX zVO*Z8yKWr#^=4(K>GjIFzikTk-;1w%S@om(_u0DNum7(9uKuCXL4kv%2@7$kbbk8( zqo3~2OWXfVM%xNb!1@J z*w6hZkrl}2EbxddW?+zQ2Vutkryo-p7?=t@T^vIy;@;j>%sb*B;CisoZ=ZR2N8&fe z?M_jlDry(q{d>cT=bqnr{=nZ#ySQC#|MUO Date: Sat, 24 Nov 2018 20:26:40 +0100 Subject: [PATCH 4/7] Fix DPI scaling for Skia tile brushes. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 48 +++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 1bfd7fd90d..076d69ae4a 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -225,10 +225,7 @@ namespace Avalonia.Skia /// public IRenderTargetBitmapImpl CreateLayer(Size size) { - var normalizedDpi = new Vector(_dpi.X / SkiaPlatform.DefaultDpi.X, _dpi.Y / SkiaPlatform.DefaultDpi.Y); - var pixelSize = size * normalizedDpi; - - return CreateRenderTarget((int) pixelSize.Width, (int) pixelSize.Height, _dpi); + return CreateRenderTarget(size); } /// @@ -387,26 +384,27 @@ namespace Avalonia.Skia /// Target size. /// Tile brush to use. /// Tile brush image. - /// The bitmap interpolation mode. private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage) { - var calc = new TileBrushCalculator(tileBrush, - new Size(tileBrushImage.PixelSize.Width, tileBrushImage.PixelSize.Height), targetSize); - - var intermediate = CreateRenderTarget( - (int)calc.IntermediateSize.Width, - (int)calc.IntermediateSize.Height, _dpi); + var calc = new TileBrushCalculator(tileBrush, tileBrushImage.PixelSize.ToSize(_dpi), targetSize); + var intermediate = CreateRenderTarget(calc.IntermediateSize); paintWrapper.AddDisposable(intermediate); using (var context = intermediate.CreateDrawingContext(null)) { - var rect = new Rect(0, 0, tileBrushImage.PixelSize.Width, tileBrushImage.PixelSize.Height); + var sourceRect = new Rect(tileBrushImage.PixelSize.ToSize(96)); + var targetRect = new Rect(tileBrushImage.PixelSize.ToSize(_dpi)); context.Clear(Colors.Transparent); context.PushClip(calc.IntermediateClip); context.Transform = calc.IntermediateTransform; - context.DrawImage(RefCountable.CreateUnownedNotClonable(tileBrushImage), 1, rect, rect, tileBrush.BitmapInterpolationMode); + context.DrawImage( + RefCountable.CreateUnownedNotClonable(tileBrushImage), + 1, + sourceRect, + targetRect, + tileBrush.BitmapInterpolationMode); context.PopClip(); } @@ -433,7 +431,14 @@ namespace Avalonia.Skia var image = intermediate.SnapshotImage(); paintWrapper.AddDisposable(image); - using (var shader = image.ToShader(tileX, tileY, tileTransform)) + var paintTransform = default(SKMatrix); + + SKMatrix.Concat( + ref paintTransform, + tileTransform, + SKMatrix.MakeScale((float)(96.0 / _dpi.X), (float)(96.0 / _dpi.Y))); + + using (var shader = image.ToShader(tileX, tileY, paintTransform)) { paintWrapper.Paint.Shader = shader; } @@ -457,7 +462,7 @@ namespace Avalonia.Skia if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1) { - var intermediate = CreateRenderTarget((int)intermediateSize.Width, (int)intermediateSize.Height, _dpi); + var intermediate = CreateRenderTarget(intermediateSize); using (var ctx = intermediate.CreateDrawingContext(visualBrushRenderer)) { @@ -609,18 +614,17 @@ namespace Avalonia.Skia /// /// Create new render target compatible with this drawing context. /// - /// Width. - /// Height. - /// Drawing dpi. + /// The size of the render target in DIPs. /// Pixel format. /// - private SurfaceRenderTarget CreateRenderTarget(int width, int height, Vector dpi, PixelFormat? format = null) + private SurfaceRenderTarget CreateRenderTarget(Size size, PixelFormat? format = null) { + var pixelSize = PixelSize.FromSize(size, _dpi); var createInfo = new SurfaceRenderTarget.CreateInfo { - Width = width, - Height = height, - Dpi = dpi, + Width = pixelSize.Width, + Height = pixelSize.Height, + Dpi = _dpi, Format = format, DisableTextLcdRendering = !_canTextUseLcdRendering, GrContext = _grContext From 53638845a64510c2afb505432b5e97e2029de6f1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 24 Nov 2018 20:54:57 +0000 Subject: [PATCH 5/7] Add more visualbrush tests. --- .../Media/VisualBrushTests.cs | 153 ++++++++++++++++++ .../VisualBrush_Grip_144_Dpi.expected.png | Bin 0 -> 177 bytes .../VisualBrush_Grip_192_Dpi.expected.png | Bin 0 -> 161 bytes .../VisualBrush_Grip_96_Dpi.expected.png | Bin 0 -> 152 bytes .../VisualBrush_Grip_144_Dpi.expected.png | Bin 0 -> 177 bytes .../VisualBrush_Grip_192_Dpi.expected.png | Bin 0 -> 161 bytes .../VisualBrush_Grip_96_Dpi.expected.png | Bin 0 -> 152 bytes 7 files changed, 153 insertions(+) create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_192_Dpi.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_96_Dpi.expected.png create mode 100644 tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png create mode 100644 tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Grip_192_Dpi.expected.png create mode 100644 tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Grip_96_Dpi.expected.png diff --git a/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs b/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs index aee83d5e0b..7c53f4516a 100644 --- a/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs @@ -450,6 +450,159 @@ namespace Avalonia.Direct2D1.RenderTests.Media CompareImages(); } + [Fact] + public async Task VisualBrush_Grip_96_Dpi() + { + var target = new Border + { + Width = 100, + Height = 10, + Background = new VisualBrush + { + SourceRect = new RelativeRect(0, 0, 4, 5, RelativeUnit.Absolute), + DestinationRect = new RelativeRect(0, 0, 4, 5, RelativeUnit.Absolute), + TileMode = TileMode.Tile, + Stretch = Stretch.UniformToFill, + Visual = new Canvas + { + Width = 4, + Height = 5, + Background = Brushes.WhiteSmoke, + Children = + { + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.LeftProperty] = 2, + }, + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.TopProperty] = 2, + }, + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.LeftProperty] = 2, + [Canvas.TopProperty] = 4, + } + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task VisualBrush_Grip_144_Dpi() + { + var target = new Border + { + Width = 100, + Height = 7.5, + Background = new VisualBrush + { + SourceRect = new RelativeRect(0, 0, 4, 5, RelativeUnit.Absolute), + DestinationRect = new RelativeRect(0, 0, 4, 5, RelativeUnit.Absolute), + TileMode = TileMode.Tile, + Stretch = Stretch.UniformToFill, + Visual = new Canvas + { + Width = 4, + Height = 5, + Background = Brushes.WhiteSmoke, + Children = + { + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.LeftProperty] = 2, + }, + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.TopProperty] = 2, + }, + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.LeftProperty] = 2, + [Canvas.TopProperty] = 4, + } + } + } + } + }; + + await RenderToFile(target, dpi: 144); + CompareImages(); + } + + [Fact] + public async Task VisualBrush_Grip_192_Dpi() + { + var target = new Border + { + Width = 100, + Height = 10, + Background = new VisualBrush + { + SourceRect = new RelativeRect(0, 0, 4, 5, RelativeUnit.Absolute), + DestinationRect = new RelativeRect(0, 0, 4, 5, RelativeUnit.Absolute), + TileMode = TileMode.Tile, + Stretch = Stretch.UniformToFill, + Visual = new Canvas + { + Width = 4, + Height = 5, + Background = Brushes.WhiteSmoke, + Children = + { + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.LeftProperty] = 2, + }, + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.TopProperty] = 2, + }, + new Rectangle + { + Width = 1, + Height = 1, + Fill = Brushes.Red, + [Canvas.LeftProperty] = 2, + [Canvas.TopProperty] = 4, + } + } + } + } + }; + + await RenderToFile(target, dpi: 192); + CompareImages(); + } + [Fact] public async Task VisualBrush_Checkerboard_96_Dpi() { diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..d530c7251963cfb32eaab6c1021bf90f819793df GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^DL~B5!3HG%HN98>q*#ibJVQ8upoSx*1IW+tba4#v z=zM$Ckgq|3r}bg_jVA(gSop5#=@dQIQ;Sd+z98%QV*ZAjGr5XNzie2)s9d^s=J>AU zXFbcLlucOn7|TDN-SNVG?foKmO-Y7zQ+D;=t)9_ee%z&U+s4?Nxv9Qkb)cOLp00i_>zopr09)=snE(I) literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_192_Dpi.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_192_Dpi.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..a209dc94cc9b721670d928d6f842bf71b209a74e GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^DL~A{!3HFqWPaBHDVAa<&kznEsNqQI0P-U|T^vI^ zI^SM3mLjB4k&yLu-XeAu&N!n0T7J7B+lDR8`m$J?Oc$JxvKPlagx8~YT y0pms8FV7yR=Wi6kjKY<(f0&D>XFZ$0zd;;mG=rzBpUXO@geCyONHeAY literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..d530c7251963cfb32eaab6c1021bf90f819793df GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^DL~B5!3HG%HN98>q*#ibJVQ8upoSx*1IW+tba4#v z=zM$Ckgq|3r}bg_jVA(gSop5#=@dQIQ;Sd+z98%QV*ZAjGr5XNzie2)s9d^s=J>AU zXFbcLlucOn7|TDN-SNVG?foKmO-Y7zQ+D;=t)9_ee%z&U+s4?Nxv9Qkb)cOLp00i_>zopr09)=snE(I) literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Grip_192_Dpi.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Grip_192_Dpi.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..a209dc94cc9b721670d928d6f842bf71b209a74e GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^DL~A{!3HFqWPaBHDVAa<&kznEsNqQI0P-U|T^vI^ zI^SM3mLjB4k&yLu-XeAu&N!n0T7J7B+lDR8`m$J?Oc$JxvKPlagx8~YT y0pms8FV7yR=Wi6kjKY<(f0&D>XFZ$0zd;;mG=rzBpUXO@geCyONHeAY literal 0 HcmV?d00001 From f82ee356bd6ff47cc521a6a06250c58268283281 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 24 Nov 2018 22:58:47 +0100 Subject: [PATCH 6/7] Added debugging util to save D2D render targets. --- .../Imaging/D2DRenderTargetBitmapImpl.cs | 19 +++++++++++++++++++ .../Avalonia.Direct2D1/Utils/DebugUtils.cs | 13 +++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/Windows/Avalonia.Direct2D1/Utils/DebugUtils.cs diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs index 72367ac50f..fbe7f840d7 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs @@ -1,8 +1,10 @@ // 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.IO; using Avalonia.Platform; using Avalonia.Rendering; +using Avalonia.Utilities; using SharpDX; using SharpDX.Direct2D1; using D2DBitmap = SharpDX.Direct2D1.Bitmap; @@ -49,5 +51,22 @@ namespace Avalonia.Direct2D1.Media.Imaging { return new OptionalDispose(_renderTarget.Bitmap, false); } + + public override void Save(Stream stream) + { + using (var wic = new WicRenderTargetBitmapImpl(PixelSize, Dpi)) + { + using (var dc = wic.CreateDrawingContext(null)) + { + dc.DrawImage( + RefCountable.CreateUnownedNotClonable(this), + 1, + new Rect(PixelSize.ToSize(Dpi.X)), + new Rect(PixelSize.ToSize(Dpi.X))); + } + + wic.Save(stream); + } + } } } diff --git a/src/Windows/Avalonia.Direct2D1/Utils/DebugUtils.cs b/src/Windows/Avalonia.Direct2D1/Utils/DebugUtils.cs new file mode 100644 index 0000000000..9ce8f15cf4 --- /dev/null +++ b/src/Windows/Avalonia.Direct2D1/Utils/DebugUtils.cs @@ -0,0 +1,13 @@ +using Avalonia.Direct2D1.Media.Imaging; + +namespace Avalonia.Direct2D1.Utils +{ + internal static class DebugUtils + { + public static void Save(SharpDX.Direct2D1.BitmapRenderTarget bitmap, string filename) + { + var rtb = new D2DRenderTargetBitmapImpl(bitmap); + rtb.Save(filename); + } + } +} From 41f201bec87d156525fd115ca35eb965520883e4 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 24 Nov 2018 23:46:15 +0100 Subject: [PATCH 7/7] Fix grip visualbrush tests. --- src/Avalonia.Visuals/Media/PixelSize.cs | 4 ++-- .../Rendering/Utilities/TileBrushCalculator.cs | 4 ++-- .../Media/DrawingContextImpl.cs | 8 +++++++- .../Avalonia.Direct2D1/Media/ImageBrushImpl.cs | 3 ++- .../VisualBrush_Grip_144_Dpi.expected.png | Bin 177 -> 271 bytes 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PixelSize.cs b/src/Avalonia.Visuals/Media/PixelSize.cs index 3d0ae0b351..6785b51716 100644 --- a/src/Avalonia.Visuals/Media/PixelSize.cs +++ b/src/Avalonia.Visuals/Media/PixelSize.cs @@ -159,8 +159,8 @@ namespace Avalonia /// The dots per inch. /// The device-independent size. public static PixelSize FromSize(Size size, Vector dpi) => new PixelSize( - (int)(size.Width * (dpi.X / 96)), - (int)(size.Height * (dpi.Y / 96))); + (int)Math.Ceiling(size.Width * (dpi.X / 96)), + (int)Math.Ceiling(size.Height * (dpi.Y / 96))); /// /// Returns the string representation of the size. diff --git a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs index 52a8fb5ab7..27949fcf55 100644 --- a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs +++ b/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs @@ -116,8 +116,8 @@ namespace Avalonia.Rendering.Utilities return true; if (SourceRect.Size.AspectRatio == _imageSize.AspectRatio) return false; - if ((int)SourceRect.Width != _imageSize.Width || - (int)SourceRect.Height != _imageSize.Height) + if (SourceRect.Width != _imageSize.Width || + SourceRect.Height != _imageSize.Height) return true; return false; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index efe26ba01a..aaca1a3b00 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -434,10 +434,16 @@ namespace Avalonia.Direct2D1.Media if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1) { + // We need to ensure the size we're requesting is an integer pixel size, otherwise + // D2D alters the DPI of the render target, which messes stuff up. PixelSize.FromSize + // will do the rounding for us. + var dpi = new Vector(_deviceContext.DotsPerInch.Width, _deviceContext.DotsPerInch.Height); + var pixelSize = PixelSize.FromSize(intermediateSize, dpi); + using (var intermediate = new BitmapRenderTarget( _deviceContext, CompatibleRenderTargetOptions.None, - intermediateSize.ToSharpDX())) + pixelSize.ToSize(dpi).ToSharpDX())) { using (var ctx = new RenderTarget(intermediate).CreateDrawingContext(_visualBrushRenderer)) { diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 2ca2a77fb8..19d320b783 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -100,7 +100,8 @@ namespace Avalonia.Direct2D1.Media using (var context = new RenderTarget(result).CreateDrawingContext(null)) { - var rect = new Rect(0, 0, bitmap.PixelSize.Width, bitmap.PixelSize.Height); + var dpi = new Vector(target.DotsPerInch.Width, target.DotsPerInch.Height); + var rect = new Rect(bitmap.PixelSize.ToSize(dpi)); context.Clear(Colors.Transparent); context.PushClip(calc.IntermediateClip); diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Grip_144_Dpi.expected.png index d530c7251963cfb32eaab6c1021bf90f819793df..1900ade5e258af8cf1d0206d405afc63e8e6e4ef 100644 GIT binary patch delta 243 zcmV^*_tiKf>^*b8_dZ(#2& zEQf_HjF=f0Pmn4fLdctRL*Tuqy9l7)F#KnAdAH*@PPpSej%=<2)pdjz^$-C!*U!We zX5T?s2IP5^5cLoVx1VL<3pv_0ETkzvzWbiLx~wP1bv9+m_>6jpw43j`5>*whdM4R6 tHg#FI!#qzY3Lak0ai^vU3+lq|Z~`{Olf9W;h1>uD002ovPDHLkV1h;GVTb?# delta 149 zcmV;G0BZk_01D- zDH#B+l)8`Idzg=R$3%Iyv0d41WldIh)MpvnmHh^SQ1O!Ua}Ek800000NkvXXu0mjf D4W~W7