diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush.cs b/src/ImageSharp.Drawing/Processing/ImageBrush.cs index 983c09adbf..c2a8d3a261 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -37,8 +38,14 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options) where TPixel : struct, IPixel { - Image specificImage = (Image)this.image; - return new ImageBrushApplicator(source, specificImage.Frames.RootFrame, region, options); + if (this.image is Image specificImage) + { + return new ImageBrushApplicator(source, specificImage, region, options, false); + } + + specificImage = this.image.CloneAs(); + + return new ImageBrushApplicator(source, specificImage, region, options, true); } /// @@ -47,10 +54,11 @@ namespace SixLabors.ImageSharp.Processing private class ImageBrushApplicator : BrushApplicator where TPixel : struct, IPixel { - /// - /// The source image. - /// - private readonly ImageFrame source; + private ImageFrame sourceFrame; + + private Image sourceImage; + + private readonly bool shouldDisposeImage; /// /// The y-length. @@ -79,10 +87,18 @@ namespace SixLabors.ImageSharp.Processing /// The image. /// The region. /// The options - public ImageBrushApplicator(ImageFrame target, ImageFrame image, RectangleF region, GraphicsOptions options) + /// Whether to dispose the image on disposal of the applicator. + public ImageBrushApplicator( + ImageFrame target, + Image image, + RectangleF region, + GraphicsOptions options, + bool shouldDisposeImage) : base(target, options) { - this.source = image; + this.sourceImage = image; + this.sourceFrame = image.Frames.RootFrame; + this.shouldDisposeImage = shouldDisposeImage; this.xLength = image.Width; this.yLength = image.Height; this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0); @@ -103,13 +119,19 @@ namespace SixLabors.ImageSharp.Processing { int srcX = (x - this.offsetX) % this.xLength; int srcY = (y - this.offsetY) % this.yLength; - return this.source[srcX, srcY]; + return this.sourceFrame[srcX, srcY]; } } /// public override void Dispose() { + if (this.shouldDisposeImage) + { + this.sourceImage?.Dispose(); + this.sourceImage = null; + this.sourceFrame = null; + } } /// @@ -124,7 +146,7 @@ namespace SixLabors.ImageSharp.Processing int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; - Span sourceRow = this.source.GetPixelRowSpan(sourceY); + Span sourceRow = this.sourceFrame.GetPixelRowSpan(sourceY); for (int i = 0; i < scanline.Length; i++) { @@ -137,7 +159,7 @@ namespace SixLabors.ImageSharp.Processing Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend( - this.source.Configuration, + this.sourceFrame.Configuration, destinationRow, destinationRow, overlaySpan, diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 1566fd0eed..db03281064 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -87,6 +87,23 @@ namespace SixLabors.ImageSharp EncodeVisitor visitor = new EncodeVisitor(encoder, stream); this.AcceptVisitor(visitor); } + + /// + /// Returns a copy of the image in the given pixel format. + /// + /// The pixel format. + /// The + public Image CloneAs() + where TPixel2 : struct, IPixel => this.CloneAs(this.Configuration); + + /// + /// Returns a copy of the image in the given pixel format. + /// + /// The pixel format. + /// The configuration providing initialization code which allows extending the library. + /// The . + public abstract Image CloneAs(Configuration configuration) + where TPixel2 : struct, IPixel; /// /// Accept a . diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 27e2bc593c..2b93b710b0 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -153,22 +153,13 @@ namespace SixLabors.ImageSharp return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); } - /// - /// Returns a copy of the image in the given pixel format. - /// - /// The pixel format. - /// The - public Image CloneAs() - where TPixel2 : struct, IPixel => this.CloneAs(this.Configuration); - /// /// Returns a copy of the image in the given pixel format. /// /// The pixel format. /// The configuration providing initialization code which allows extending the library. /// The . - public Image CloneAs(Configuration configuration) - where TPixel2 : struct, IPixel + public override Image CloneAs(Configuration configuration) { IEnumerable> clonedFrames = this.Frames.Select(x => x.CloneAs(configuration)); return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 70879e6feb..e48a0b0ae3 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -175,5 +175,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing } } } + + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs index e1fc7c30c1..d33d7b70d2 100644 --- a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs @@ -32,5 +32,24 @@ namespace SixLabors.ImageSharp.Tests.Drawing } } } + + [Theory] + [WithTestPatternImages(200, 200, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void UseBrushOfDifferentPixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + byte[] data = TestFile.Create(TestImages.Png.Ducky).Bytes; + using (Image background = provider.GetImage()) + using (Image overlay = provider.PixelType == PixelTypes.Rgba32 + ? (Image)Image.Load(data) + : Image.Load(data)) + { + var brush = new ImageBrush(overlay); + background.Mutate(c => c.Fill(brush)); + + background.DebugSave(provider, appendSourceFileOrDescription : false); + background.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index c91ef56a1a..92cc9f6368 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -152,21 +152,19 @@ namespace SixLabors.ImageSharp.Tests /// /// Encodes image by the format matching the required extension, than saves it to the recommended output file. /// - /// The pixel format of the image /// The image instance /// The requested extension /// Optional encoder /// A value indicating whether to append the pixel type to the test output file name /// A boolean indicating whether to append to the test output file name. /// Additional information to append to the test output file name - public string SaveTestOutputFile( - Image image, + public string SaveTestOutputFile( + Image image, string extension = null, IImageEncoder encoder = null, object testOutputDetails = null, bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true) - where TPixel : struct, IPixel { string path = this.GetTestOutputFileName( extension, diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 048c6347cd..e77b1974d3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -55,17 +55,16 @@ namespace SixLabors.ImageSharp.Tests }); } - public static Image DebugSave( - this Image image, + public static void DebugSave( + this Image image, ITestImageProvider provider, FormattableString testOutputDetails, string extension = "png", bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true, IImageEncoder encoder = null) - where TPixel : struct, IPixel { - return image.DebugSave( + image.DebugSave( provider, (object)testOutputDetails, extension, @@ -77,7 +76,6 @@ namespace SixLabors.ImageSharp.Tests /// /// Saves the image only when not running in the CI server. /// - /// The pixel format /// The image /// The image provider /// Details to be concatenated to the test output file, describing the parameters of the test. @@ -85,15 +83,14 @@ namespace SixLabors.ImageSharp.Tests /// A boolean indicating whether to append the pixel type to the output file name. /// A boolean indicating whether to append to the test output file name. /// Custom encoder to use. - public static Image DebugSave( - this Image image, + public static Image DebugSave( + this Image image, ITestImageProvider provider, object testOutputDetails = null, string extension = "png", bool appendPixelTypeToFileName = true, bool appendSourceFileOrDescription = true, IImageEncoder encoder = null) - where TPixel : struct, IPixel { if (TestEnvironment.RunsOnCI) { @@ -111,37 +108,34 @@ namespace SixLabors.ImageSharp.Tests return image; } - public static Image DebugSave( - this Image image, + public static void DebugSave( + this Image image, ITestImageProvider provider, IImageEncoder encoder, FormattableString testOutputDetails, bool appendPixelTypeToFileName = true) - where TPixel : struct, IPixel { - return image.DebugSave(provider, encoder, (object)testOutputDetails, appendPixelTypeToFileName); + image.DebugSave(provider, encoder, (object)testOutputDetails, appendPixelTypeToFileName); } /// /// Saves the image only when not running in the CI server. /// - /// The pixel format /// The image /// The image provider /// The image encoder /// Details to be concatenated to the test output file, describing the parameters of the test. /// A boolean indicating whether to append the pixel type to the output file name. - public static Image DebugSave( - this Image image, + public static void DebugSave( + this Image image, ITestImageProvider provider, IImageEncoder encoder, object testOutputDetails = null, bool appendPixelTypeToFileName = true) - where TPixel : struct, IPixel { if (TestEnvironment.RunsOnCI) { - return image; + return; } // We are running locally then we want to save it out @@ -150,7 +144,6 @@ namespace SixLabors.ImageSharp.Tests encoder: encoder, testOutputDetails: testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName); - return image; } public static Image DebugSaveMultiFrame( diff --git a/tests/Images/External b/tests/Images/External index 55c250ab80..9d1d9dd537 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 55c250ab80f7f9fc26208b9b9d9590cbe8012446 +Subproject commit 9d1d9dd53755007ee812907f64304d1925c6a91a