Repository: SixLabors/ImageSharp

Issue:
Add DrawImage(Image, Point, Rectangle) method to the DrawImage processor
Shouldn't be hard to implement and would have better performance than `Clone -> Crop -> Draw` path.

From discussion by @TheBoneJarmer in https://github.com/SixLabors/ImageSharp/discussions/2102


Hints:

Patch:
diff --git a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
index 2cb8dc3211..ad93d6f167 100644
--- a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
@@ -12,168 +12,280 @@ namespace SixLabors.ImageSharp.Processing;
 public static class DrawImageExtensions
 {
     /// <summary>
-    /// Draws the given image together with the current one by blending their pixels.
+    /// Draws the given image together with the currently processing image by blending their pixels.
     /// </summary>
-    /// <param name="source">The image this method extends.</param>
-    /// <param name="image">The image to blend with the currently processing image.</param>
-    /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
-    /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
     public static IImageProcessingContext DrawImage(
         this IImageProcessingContext source,
         Image image,
         float opacity)
     {
-        var options = source.GetGraphicsOptions();
-        return source.ApplyProcessor(
-            new DrawImageProcessor(
-            image,
-            Point.Empty,
-            options.ColorBlendingMode,
-            options.AlphaCompositionMode,
-            opacity));
+        GraphicsOptions options = source.GetGraphicsOptions();
+        return DrawImage(source, image, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
     }
 
     /// <summary>
-    /// Draws the given image together with the current one by blending their pixels.
+    /// Draws the given image together with the currently processing image by blending their pixels.
     /// </summary>
-    /// <param name="source">The image this method extends.</param>
-    /// <param name="image">The image to blend with the currently processing image.</param>
-    /// <param name="colorBlending">The blending mode.</param>
-    /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
-    /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
+    public static IImageProcessingContext DrawImage(
+        this IImageProcessingContext source,
+        Image image,
+        Rectangle rectangle,
+        float opacity)
+    {
+        GraphicsOptions options = source.GetGraphicsOptions();
+        return DrawImage(source, image, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
+    }
+
+    /// <summary>
+    /// Draws the given image together with the currently processing image by blending their pixels.
+    /// </summary>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="colorBlending">The color blending mode.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
     public static IImageProcessingContext DrawImage(
         this IImageProcessingContext source,
         Image image,
         PixelColorBlendingMode colorBlending,
-        float opacity) =>
-        source.ApplyProcessor(
-            new DrawImageProcessor(
-                image,
-                Point.Empty,
-                colorBlending,
-                source.GetGraphicsOptions().AlphaCompositionMode,
-                opacity));
+        float opacity)
+        => DrawImage(source, image, Point.Empty, colorBlending, opacity);
 
     /// <summary>
-    /// Draws the given image together with the current one by blending their pixels.
+    /// Draws the given image together with the currently processing image by blending their pixels.
     /// </summary>
-    /// <param name="source">The image this method extends.</param>
-    /// <param name="image">The image to blend with the currently processing image.</param>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
+    /// <param name="colorBlending">The color blending mode.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
+    public static IImageProcessingContext DrawImage(
+        this IImageProcessingContext source,
+        Image image,
+        Rectangle rectangle,
+        PixelColorBlendingMode colorBlending,
+        float opacity)
+        => DrawImage(source, image, rectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
+
+    /// <summary>
+    /// Draws the given image together with the currently processing image by blending their pixels.
+    /// </summary>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
     /// <param name="colorBlending">The color blending mode.</param>
     /// <param name="alphaComposition">The alpha composition mode.</param>
-    /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
-    /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
     public static IImageProcessingContext DrawImage(
         this IImageProcessingContext source,
         Image image,
         PixelColorBlendingMode colorBlending,
         PixelAlphaCompositionMode alphaComposition,
-        float opacity) =>
-        source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity));
+        float opacity)
+        => DrawImage(source, image, Point.Empty, colorBlending, alphaComposition, opacity);
+
+    /// <summary>
+    /// Draws the given image together with the currently processing image by blending their pixels.
+    /// </summary>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
+    /// <param name="colorBlending">The color blending mode.</param>
+    /// <param name="alphaComposition">The alpha composition mode.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
+    public static IImageProcessingContext DrawImage(
+        this IImageProcessingContext source,
+        Image image,
+        Rectangle rectangle,
+        PixelColorBlendingMode colorBlending,
+        PixelAlphaCompositionMode alphaComposition,
+        float opacity)
+        => DrawImage(source, image, Point.Empty, rectangle, colorBlending, alphaComposition, opacity);
 
     /// <summary>
-    /// Draws the given image together with the current one by blending their pixels.
+    /// Draws the given image together with the currently processing image by blending their pixels.
     /// </summary>
-    /// <param name="source">The image this method extends.</param>
-    /// <param name="image">The image to blend with the currently processing image.</param>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
     /// <param name="options">The options, including the blending type and blending amount.</param>
-    /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
     public static IImageProcessingContext DrawImage(
         this IImageProcessingContext source,
         Image image,
-        GraphicsOptions options) =>
-        source.ApplyProcessor(
-            new DrawImageProcessor(
-                image,
-                Point.Empty,
-                options.ColorBlendingMode,
-                options.AlphaCompositionMode,
-                options.BlendPercentage));
+        GraphicsOptions options)
+        => DrawImage(source, image, Point.Empty, options);
+
+    /// <summary>
+    /// Draws the given image together with the currently processing image by blending their pixels.
+    /// </summary>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
+    /// <param name="options">The options, including the blending type and blending amount.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
+    public static IImageProcessingContext DrawImage(
+        this IImageProcessingContext source,
+        Image image,
+        Rectangle rectangle,
+        GraphicsOptions options)
+        => DrawImage(source, image, Point.Empty, rectangle, options);
+
+    /// <summary>
+    /// Draws the given image together with the currently processing image by blending their pixels.
+    /// </summary>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="location">The location on the currenty processing image at which to draw.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
+    public static IImageProcessingContext DrawImage(
+        this IImageProcessingContext source,
+        Image image,
+        Point location,
+        float opacity)
+    {
+        GraphicsOptions options = source.GetGraphicsOptions();
+        return DrawImage(source, image, location, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
+    }
 
     /// <summary>
-    /// Draws the given image together with the current one by blending their pixels.
+    /// Draws the given image together with the currently processing image by blending their pixels.
     /// </summary>
-    /// <param name="source">The image this method extends.</param>
-    /// <param name="image">The image to blend with the currently processing image.</param>
-    /// <param name="location">The location to draw the blended image.</param>
-    /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
-    /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="location">The location on the currenty processing image at which to draw.</param>
+    /// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
     public static IImageProcessingContext DrawImage(
         this IImageProcessingContext source,
         Image image,
         Point location,
+        Rectangle rectangle,
         float opacity)
     {
-        var options = source.GetGraphicsOptions();
-        return source.ApplyProcessor(
-            new DrawImageProcessor(
-            image,
-            location,
-            options.ColorBlendingMode,
-            options.AlphaCompositionMode,
-            opacity));
+        GraphicsOptions options = source.GetGraphicsOptions();
+        return DrawImage(source, image, location, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
     }
 
     /// <summary>
-    /// Draws the given image together with the current one by blending their pixels.
+    /// Draws the given image together with the currently processing image by blending their pixels.
     /// </summary>
-    /// <param name="source">The image this method extends.</param>
-    /// <param name="image">The image to blend with the currently processing image.</param>
-    /// <param name="location">The location to draw the blended image.</param>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="location">The location on the currenty processing image at which to draw.</param>
     /// <param name="colorBlending">The color blending to apply.</param>
-    /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
-    /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
     public static IImageProcessingContext DrawImage(
         this IImageProcessingContext source,
         Image image,
         Point location,
         PixelColorBlendingMode colorBlending,
-        float opacity) =>
-        source.ApplyProcessor(
-            new DrawImageProcessor(
-                image,
-                location,
-                colorBlending,
-                source.GetGraphicsOptions().AlphaCompositionMode,
-                opacity));
+        float opacity)
+        => DrawImage(source, image, location, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
 
     /// <summary>
-    /// Draws the given image together with the current one by blending their pixels.
+    /// Draws the given image together with the currently processing image by blending their pixels.
     /// </summary>
-    /// <param name="source">The image this method extends.</param>
-    /// <param name="image">The image to blend with the currently processing image.</param>
-    /// <param name="location">The location to draw the blended image.</param>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="location">The location on the currenty processing image at which to draw.</param>
+    /// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
+    /// <param name="colorBlending">The color blending to apply.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
+    public static IImageProcessingContext DrawImage(
+        this IImageProcessingContext source,
+        Image image,
+        Point location,
+        Rectangle rectangle,
+        PixelColorBlendingMode colorBlending,
+        float opacity)
+        => DrawImage(source, image, location, rectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
+
+    /// <summary>
+    /// Draws the given image together with the currently processing image by blending their pixels.
+    /// </summary>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="location">The location on the currenty processing image at which to draw.</param>
+    /// <param name="options">The options containing the blend mode and opacity.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
+    public static IImageProcessingContext DrawImage(
+        this IImageProcessingContext source,
+        Image image,
+        Point location,
+        GraphicsOptions options)
+        => DrawImage(source, image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
+
+    /// <summary>
+    /// Draws the given image together with the currently processing image by blending their pixels.
+    /// </summary>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="location">The location on the currenty processing image at which to draw.</param>
+    /// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
+    /// <param name="options">The options containing the blend mode and opacity.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
+    public static IImageProcessingContext DrawImage(
+        this IImageProcessingContext source,
+        Image image,
+        Point location,
+        Rectangle rectangle,
+        GraphicsOptions options)
+        => DrawImage(source, image, location, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
+
+    /// <summary>
+    /// Draws the given image together with the currently processing image by blending their pixels.
+    /// </summary>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="location">The location on the currenty processing image at which to draw.</param>
     /// <param name="colorBlending">The color blending to apply.</param>
     /// <param name="alphaComposition">The alpha composition mode.</param>
-    /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
-    /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
     public static IImageProcessingContext DrawImage(
         this IImageProcessingContext source,
         Image image,
         Point location,
         PixelColorBlendingMode colorBlending,
         PixelAlphaCompositionMode alphaComposition,
-        float opacity) =>
-        source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity));
+        float opacity)
+        => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity));
 
     /// <summary>
-    /// Draws the given image together with the current one by blending their pixels.
+    /// Draws the given image together with the currently processing image by blending their pixels.
     /// </summary>
-    /// <param name="source">The image this method extends.</param>
-    /// <param name="image">The image to blend with the currently processing image.</param>
-    /// <param name="location">The location to draw the blended image.</param>
-    /// <param name="options">The options containing the blend mode and opacity.</param>
-    /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
+    /// <param name="source">The current image processing context.</param>
+    /// <param name="image">The image to draw on the currently processing image.</param>
+    /// <param name="location">The location on the currenty processing image at which to draw.</param>
+    /// <param name="rectangle">The rectangle structure that specifies the portion of the image to draw.</param>
+    /// <param name="colorBlending">The color blending to apply.</param>
+    /// <param name="alphaComposition">The alpha composition mode.</param>
+    /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
+    /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
     public static IImageProcessingContext DrawImage(
         this IImageProcessingContext source,
         Image image,
         Point location,
-        GraphicsOptions options) =>
+        Rectangle rectangle,
+        PixelColorBlendingMode colorBlending,
+        PixelAlphaCompositionMode alphaComposition,
+        float opacity) =>
         source.ApplyProcessor(
-            new DrawImageProcessor(
-                image,
-                location,
-                options.ColorBlendingMode,
-                options.AlphaCompositionMode,
-                options.BlendPercentage));
+            new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity),
+            rectangle);
 }
diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
index b7d5b42f8a..9de4f862b8 100644
--- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
@@ -63,7 +63,7 @@ public DrawImageProcessor(
     public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>(Configuration configuration, Image<TPixelBg> source, Rectangle sourceRectangle)
         where TPixelBg : unmanaged, IPixel<TPixelBg>
     {
-        var visitor = new ProcessorFactoryVisitor<TPixelBg>(configuration, this, source, sourceRectangle);
+        ProcessorFactoryVisitor<TPixelBg> visitor = new(configuration, this, source, sourceRectangle);
         this.Image.AcceptVisitor(visitor);
         return visitor.Result;
     }
@@ -88,8 +88,7 @@ public ProcessorFactoryVisitor(Configuration configuration, DrawImageProcessor d
 
         public void Visit<TPixelFg>(Image<TPixelFg> image)
             where TPixelFg : unmanaged, IPixel<TPixelFg>
-        {
-            this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
+            => this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
                 this.configuration,
                 image,
                 this.source,
@@ -98,6 +97,5 @@ public void Visit<TPixelFg>(Image<TPixelFg> image)
                 this.definition.ColorBlendingMode,
                 this.definition.AlphaCompositionMode,
                 this.definition.Opacity);
-        }
     }
 }
diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
index 82e639f1bd..436a447972 100644
--- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
+++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
@@ -89,7 +89,7 @@ protected override void OnFrameApply(ImageFrame<TPixelBg> source)
 
         int width = maxX - minX;
 
-        var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
+        Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
 
         // Not a valid operation because rectangle does not overlap with this image.
         if (workingRect.Width <= 0 || workingRect.Height <= 0)
@@ -98,7 +98,7 @@ protected override void OnFrameApply(ImageFrame<TPixelBg> source)
                 "Cannot draw image because the source image does not overlap the target image.");
         }
 
-        var operation = new RowOperation(source.PixelBuffer, targetImage.Frames.RootFrame.PixelBuffer, blender, configuration, minX, width, locationY, targetX, this.Opacity);
+        DrawImageProcessor<TPixelBg, TPixelFg>.RowOperation operation = new(source.PixelBuffer, targetImage.Frames.RootFrame.PixelBuffer, blender, configuration, minX, width, locationY, targetX, this.Opacity);
         ParallelRowIterator.IterateRows(
             configuration,
             workingRect,


Test patch:
diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
index 0dcb961f84..d017e5ad42 100644
--- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
@@ -119,9 +119,7 @@ public void DrawImageOfDifferentPixelType<TPixel>(TestImageProvider<TPixel> prov
     public void WorksWithDifferentLocations(TestImageProvider<Rgba32> provider, int x, int y)
     {
         using Image<Rgba32> background = provider.GetImage();
-        using Image<Rgba32> overlay = new(50, 50);
-        Assert.True(overlay.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> overlayMem));
-        overlayMem.Span.Fill(Color.Black);
+        using Image<Rgba32> overlay = new(50, 50, Color.Black.ToRgba32());
 
         background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F));
 
@@ -138,6 +136,31 @@ public void WorksWithDifferentLocations(TestImageProvider<Rgba32> provider, int
             appendSourceFileOrDescription: false);
     }
 
+    [Theory]
+    [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 10, 10)]
+    [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 50, 25)]
+    [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 25, 50)]
+    [WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 50, 50)]
+    public void WorksWithDifferentBounds(TestImageProvider<Rgba32> provider, int width, int height)
+    {
+        using Image<Rgba32> background = provider.GetImage();
+        using Image<Rgba32> overlay = new(50, 50, Color.Black.ToRgba32());
+
+        background.Mutate(c => c.DrawImage(overlay, new Rectangle(0, 0, width, height), PixelColorBlendingMode.Normal, 1F));
+
+        background.DebugSave(
+            provider,
+            testOutputDetails: $"{width}_{height}",
+            appendPixelTypeToFileName: false,
+            appendSourceFileOrDescription: false);
+
+        background.CompareToReferenceOutput(
+            provider,
+            testOutputDetails: $"{width}_{height}",
+            appendPixelTypeToFileName: false,
+            appendSourceFileOrDescription: false);
+    }
+
     [Theory]
     [WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)]
     public void DrawTransformed<TPixel>(TestImageProvider<TPixel> provider)
@@ -158,12 +181,12 @@ public void DrawTransformed<TPixel>(TestImageProvider<TPixel> provider)
         Point position = new((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2);
         image.Mutate(x => x.DrawImage(blend, position, .75F));
 
-        image.DebugSave(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false);
+        image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false);
         image.CompareToReferenceOutput(
             ImageComparer.TolerantPercentage(0.002f),
             provider,
-            appendSourceFileOrDescription: false,
-            appendPixelTypeToFileName: false);
+            appendPixelTypeToFileName: false,
+            appendSourceFileOrDescription: false);
     }
 
     [Theory]
@@ -179,9 +202,6 @@ public void NonOverlappingImageThrows(TestImageProvider<Rgba32> provider, int x,
 
         Assert.Contains("does not overlap", ex.ToString());
 
-        void Test()
-        {
-            background.Mutate(context => context.DrawImage(overlay, new Point(x, y), new GraphicsOptions()));
-        }
+        void Test() => background.Mutate(context => context.DrawImage(overlay, new Point(x, y), new GraphicsOptions()));
     }
 }
diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_10_10.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_10_10.png
new file mode 100644
index 0000000000..64ce14d509
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_10_10.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f353ee06664a6ff27c533af63cffb9eac4917103ba0b7fff9084fb4d2d42fed7
+size 155
diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_25_50.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_25_50.png
new file mode 100644
index 0000000000..7e0466186a
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_25_50.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:af198da8f611eb97f3e4e358cb097cd292771d52e991de76495f073c1f1b9338
+size 154
diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_25.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_25.png
new file mode 100644
index 0000000000..6b93e597eb
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_25.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:35bda87f28e03c5ed0d45412e367fae9b04b7684f5242dc20e7709e8ed71cd86
+size 156
diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_50.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_50.png
new file mode 100644
index 0000000000..540082dfdf
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/WorksWithDifferentBounds_50_50.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f8b9ff745592bb0e0a365cb0985a5519bf567fc73a09211c88bcda51356f9202
+size 155