From 3ee51ef977f2f539e0c0bacd61c0c2db6cc6b2b8 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 6 Dec 2023 19:19:56 +1000 Subject: [PATCH] Expose source region constructor. --- .../Processing/ImageBrush.cs | 13 ++-- .../Drawing/FillImageBrushTests.cs | 63 +++++++++++++++++-- .../CanDrawNegativeOffsetImage_Rgba32.png | 3 + 3 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 tests/Images/ReferenceOutput/Drawing/FillImageBrushTests/CanDrawNegativeOffsetImage_Rgba32.png diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush.cs b/src/ImageSharp.Drawing/Processing/ImageBrush.cs index 47826411..5d8075e3 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush.cs @@ -24,7 +24,7 @@ public class ImageBrush : Brush /// /// Initializes a new instance of the class. /// - /// The image. + /// The source image to draw. public ImageBrush(Image image) : this(image, image.Bounds) { @@ -33,15 +33,12 @@ public ImageBrush(Image image) /// /// Initializes a new instance of the class. /// - /// The image. - /// - /// The region of interest. - /// This overrides any region used to initialize the brush applicator. - /// - internal ImageBrush(Image image, RectangleF region) + /// The source image. + /// The region of interest within the source image to draw. + public ImageBrush(Image image, RectangleF region) { this.image = image; - this.region = region; + this.region = RectangleF.Intersect(image.Bounds, region); } /// diff --git a/tests/ImageSharp.Drawing.Tests/Drawing/FillImageBrushTests.cs b/tests/ImageSharp.Drawing.Tests/Drawing/FillImageBrushTests.cs index 62f27ffd..ec2e3e6a 100644 --- a/tests/ImageSharp.Drawing.Tests/Drawing/FillImageBrushTests.cs +++ b/tests/ImageSharp.Drawing.Tests/Drawing/FillImageBrushTests.cs @@ -14,10 +14,10 @@ public class FillImageBrushTests [Fact] public void DoesNotDisposeImage() { - using (var src = new Image(5, 5)) + using (Image src = new(5, 5)) { - var brush = new ImageBrush(src); - using (var dest = new Image(10, 10)) + ImageBrush brush = new(src); + using (Image dest = new(10, 10)) { dest.Mutate(c => c.Fill(brush, new Rectangle(0, 0, 10, 10))); dest.Mutate(c => c.Fill(brush, new Rectangle(0, 0, 10, 10))); @@ -36,7 +36,7 @@ public void UseBrushOfDifferentPixelType(TestImageProvider provi ? Image.Load(data) : Image.Load(data); - var brush = new ImageBrush(overlay); + ImageBrush brush = new(overlay); background.Mutate(c => c.Fill(brush)); background.DebugSave(provider, appendSourceFileOrDescription: false); @@ -54,7 +54,7 @@ public void CanDrawLandscapeImage(TestImageProvider provider) overlay.Mutate(c => c.Crop(new Rectangle(0, 0, 125, 90))); - var brush = new ImageBrush(overlay); + ImageBrush brush = new(overlay); background.Mutate(c => c.Fill(brush)); background.DebugSave(provider, appendSourceFileOrDescription: false); @@ -72,10 +72,61 @@ public void CanDrawPortraitImage(TestImageProvider provider) overlay.Mutate(c => c.Crop(new Rectangle(0, 0, 90, 125))); - var brush = new ImageBrush(overlay); + ImageBrush brush = new(overlay); background.Mutate(c => c.Fill(brush)); background.DebugSave(provider, appendSourceFileOrDescription: false); background.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false); } + + [Theory] + [WithSolidFilledImages(1000, 1000, "White", PixelTypes.Rgba32)] + public void CanDrawNegativeOffsetImage(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + byte[] data = TestFile.Create(TestImages.Png.Ducky).Bytes; + using Image background = provider.GetImage(); + using Image overlay = Image.Load(data); + + overlay.Mutate(c => c.Resize(100, 100)); + + ImageBrush halfBrush = new(overlay, new RectangleF(50, 0, 50, 100)); + ImageBrush fullBrush = new(overlay); + background.Mutate(c => DrawFull(c, new Size(100, 100), fullBrush, halfBrush, background.Width, background.Height)); + + background.DebugSave(provider, appendSourceFileOrDescription: false); + background.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false); + } + + private static void DrawFull(IImageProcessingContext ctx, Size size, ImageBrush brush, ImageBrush halfBrush, int width, int height) + { + int j = 0; + while (j < height) + { + bool half = false; + int limitWidth = width; + int i = 0; + if ((j / size.Height) % 2 != 0) + { + half = true; + } + + while (i < limitWidth) + { + if (half) + { + ctx.Fill(halfBrush, new RectangleF(i, j, size.Width / 2f, size.Height)); + i += (int)(size.Width / 2f); + half = false; + } + else + { + ctx.Fill(brush, new RectangleF(new PointF(i, j), size)); + i += size.Width; + } + } + + j += size.Height; + } + } } diff --git a/tests/Images/ReferenceOutput/Drawing/FillImageBrushTests/CanDrawNegativeOffsetImage_Rgba32.png b/tests/Images/ReferenceOutput/Drawing/FillImageBrushTests/CanDrawNegativeOffsetImage_Rgba32.png new file mode 100644 index 00000000..3a4538c8 --- /dev/null +++ b/tests/Images/ReferenceOutput/Drawing/FillImageBrushTests/CanDrawNegativeOffsetImage_Rgba32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cad09068867e5c56874dd5b44937fd22a386d27ff82e6c5d974444512f1950a +size 100398