diff --git a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
index ad93d6f16..25e504831 100644
--- a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
@@ -15,277 +15,277 @@ public static class DrawImageExtensions
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
+ /// The image to draw on the currently processing image.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
+ Image foreground,
float opacity)
{
GraphicsOptions options = source.GetGraphicsOptions();
- return DrawImage(source, image, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
+ return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
}
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The rectangle structure that specifies the portion of the image to draw.
+ /// The image to draw on the currently processing image.
+ /// The rectangle structure that specifies the portion of the image to draw.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Rectangle rectangle,
+ Image foreground,
+ Rectangle foregroundRectangle,
float opacity)
{
GraphicsOptions options = source.GetGraphicsOptions();
- return DrawImage(source, image, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
+ return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
}
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
+ /// The image to draw on the currently processing image.
/// The color blending mode.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
+ Image foreground,
PixelColorBlendingMode colorBlending,
float opacity)
- => DrawImage(source, image, Point.Empty, colorBlending, opacity);
+ => DrawImage(source, foreground, Point.Empty, colorBlending, opacity);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The rectangle structure that specifies the portion of the image to draw.
+ /// The image to draw on the currently processing image.
+ /// The rectangle structure that specifies the portion of the image to draw.
/// The color blending mode.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Rectangle rectangle,
+ Image foreground,
+ Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
float opacity)
- => DrawImage(source, image, rectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
+ => DrawImage(source, foreground, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
+ /// The image to draw on the currently processing image.
/// The color blending mode.
/// The alpha composition mode.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
+ Image foreground,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity)
- => DrawImage(source, image, Point.Empty, colorBlending, alphaComposition, opacity);
+ => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The rectangle structure that specifies the portion of the image to draw.
+ /// The image to draw on the currently processing image.
+ /// The rectangle structure that specifies the portion of the image to draw.
/// The color blending mode.
/// The alpha composition mode.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Rectangle rectangle,
+ Image foreground,
+ Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity)
- => DrawImage(source, image, Point.Empty, rectangle, colorBlending, alphaComposition, opacity);
+ => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
+ /// The image to draw on the currently processing image.
/// The options, including the blending type and blending amount.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
+ Image foreground,
GraphicsOptions options)
- => DrawImage(source, image, Point.Empty, options);
+ => DrawImage(source, foreground, Point.Empty, options);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The rectangle structure that specifies the portion of the image to draw.
+ /// The image to draw on the currently processing image.
+ /// The rectangle structure that specifies the portion of the image to draw.
/// The options, including the blending type and blending amount.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Rectangle rectangle,
+ Image foreground,
+ Rectangle foregroundRectangle,
GraphicsOptions options)
- => DrawImage(source, image, Point.Empty, rectangle, options);
+ => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The location on the currenty processing image at which to draw.
+ /// The image to draw on the currently processing image.
+ /// The location on the currently processing image at which to draw.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Point location,
+ Image foreground,
+ Point backgroundLocation,
float opacity)
{
GraphicsOptions options = source.GetGraphicsOptions();
- return DrawImage(source, image, location, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
+ return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
}
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The location on the currenty processing image at which to draw.
- /// The rectangle structure that specifies the portion of the image to draw.
+ /// The image to draw on the currently processing image.
+ /// The location on the currently processing image at which to draw.
+ /// The rectangle structure that specifies the portion of the image to draw.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Point location,
- Rectangle rectangle,
+ Image foreground,
+ Point backgroundLocation,
+ Rectangle foregroundRectangle,
float opacity)
{
GraphicsOptions options = source.GetGraphicsOptions();
- return DrawImage(source, image, location, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
+ return DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
}
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The location on the currenty processing image at which to draw.
+ /// The image to draw on the currently processing image.
+ /// The location on the currently processing image at which to draw.
/// The color blending to apply.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Point location,
+ Image foreground,
+ Point backgroundLocation,
PixelColorBlendingMode colorBlending,
float opacity)
- => DrawImage(source, image, location, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
+ => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The location on the currenty processing image at which to draw.
- /// The rectangle structure that specifies the portion of the image to draw.
+ /// The image to draw on the currently processing image.
+ /// The location on the currently processing image at which to draw.
+ /// The rectangle structure that specifies the portion of the image to draw.
/// The color blending to apply.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Point location,
- Rectangle rectangle,
+ Image foreground,
+ Point backgroundLocation,
+ Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
float opacity)
- => DrawImage(source, image, location, rectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
+ => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The location on the currenty processing image at which to draw.
+ /// The image to draw on the currently processing image.
+ /// The location on the currently processing image at which to draw.
/// The options containing the blend mode and opacity.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Point location,
+ Image foreground,
+ Point backgroundLocation,
GraphicsOptions options)
- => DrawImage(source, image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
+ => DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The location on the currenty processing image at which to draw.
- /// The rectangle structure that specifies the portion of the image to draw.
+ /// The image to draw on the currently processing image.
+ /// The location on the currently processing image at which to draw.
+ /// The rectangle structure that specifies the portion of the image to draw.
/// The options containing the blend mode and opacity.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Point location,
- Rectangle rectangle,
+ Image foreground,
+ Point backgroundLocation,
+ Rectangle foregroundRectangle,
GraphicsOptions options)
- => DrawImage(source, image, location, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
+ => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The location on the currenty processing image at which to draw.
+ /// The image to draw on the currently processing image.
+ /// The location on the currently processing image at which to draw.
/// The color blending to apply.
/// The alpha composition mode.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Point location,
+ Image foreground,
+ Point backgroundLocation,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity)
- => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity));
+ => source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity));
///
/// Draws the given image together with the currently processing image by blending their pixels.
///
/// The current image processing context.
- /// The image to draw on the currently processing image.
- /// The location on the currenty processing image at which to draw.
- /// The rectangle structure that specifies the portion of the image to draw.
+ /// The image to draw on the currently processing image.
+ /// The location on the currently processing image at which to draw.
+ /// The rectangle structure that specifies the portion of the image to draw.
/// The color blending to apply.
/// The alpha composition mode.
/// The opacity of the image to draw. Must be between 0 and 1.
/// The .
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
- Image image,
- Point location,
- Rectangle rectangle,
+ Image foreground,
+ Point backgroundLocation,
+ Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
source.ApplyProcessor(
- new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity),
- rectangle);
+ new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity),
+ foregroundRectangle);
}
diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
index 88b59b7dc..6ecf16fc6 100644
--- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
@@ -14,20 +14,41 @@ public class DrawImageProcessor : IImageProcessor
///
/// Initializes a new instance of the class.
///
- /// The image to blend.
- /// The location to draw the blended image.
+ /// The image to blend.
+ /// The location to draw the foreground image on the background.
/// The blending mode to use when drawing the image.
/// The Alpha blending mode to use when drawing the image.
/// The opacity of the image to blend.
public DrawImageProcessor(
- Image image,
- Point location,
+ Image foreground,
+ Point backgroundLocation,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
+ : this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity)
{
- this.Image = image;
- this.Location = location;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The image to blend.
+ /// The location to draw the foreground image on the background.
+ /// The rectangular portion of the foreground image to draw.
+ /// The blending mode to use when drawing the image.
+ /// The Alpha blending mode to use when drawing the image.
+ /// The opacity of the image to blend.
+ public DrawImageProcessor(
+ Image foreground,
+ Point backgroundLocation,
+ Rectangle foregroundRectangle,
+ PixelColorBlendingMode colorBlendingMode,
+ PixelAlphaCompositionMode alphaCompositionMode,
+ float opacity)
+ {
+ this.ForeGround = foreground;
+ this.BackgroundLocation = backgroundLocation;
+ this.ForegroundRectangle = foregroundRectangle;
this.ColorBlendingMode = colorBlendingMode;
this.AlphaCompositionMode = alphaCompositionMode;
this.Opacity = opacity;
@@ -36,12 +57,17 @@ public class DrawImageProcessor : IImageProcessor
///
/// Gets the image to blend.
///
- public Image Image { get; }
+ public Image ForeGround { get; }
+
+ ///
+ /// Gets the location to draw the foreground image on the background.
+ ///
+ public Point BackgroundLocation { get; }
///
- /// Gets the location to draw the blended image.
+ /// Gets the rectangular portion of the foreground image to draw.
///
- public Point Location { get; }
+ public Rectangle ForegroundRectangle { get; }
///
/// Gets the blending mode to use when drawing the image.
@@ -62,8 +88,8 @@ public class DrawImageProcessor : IImageProcessor
public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixelBg : unmanaged, IPixel
{
- ProcessorFactoryVisitor visitor = new(configuration, this, source, sourceRectangle);
- this.Image.AcceptVisitor(visitor);
+ ProcessorFactoryVisitor visitor = new(configuration, this, source);
+ this.ForeGround.AcceptVisitor(visitor);
return visitor.Result!;
}
@@ -73,14 +99,15 @@ public class DrawImageProcessor : IImageProcessor
private readonly Configuration configuration;
private readonly DrawImageProcessor definition;
private readonly Image source;
- private readonly Rectangle sourceRectangle;
- public ProcessorFactoryVisitor(Configuration configuration, DrawImageProcessor definition, Image source, Rectangle sourceRectangle)
+ public ProcessorFactoryVisitor(
+ Configuration configuration,
+ DrawImageProcessor definition,
+ Image source)
{
this.configuration = configuration;
this.definition = definition;
this.source = source;
- this.sourceRectangle = sourceRectangle;
}
public IImageProcessor? Result { get; private set; }
@@ -91,8 +118,8 @@ public class DrawImageProcessor : IImageProcessor
this.configuration,
image,
this.source,
- this.sourceRectangle,
- this.definition.Location,
+ this.definition.BackgroundLocation,
+ this.definition.ForegroundRectangle,
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 436a44797..378ea20fa 100644
--- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
+++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
@@ -21,36 +21,42 @@ internal class DrawImageProcessor : ImageProcessor
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
- /// The foreground to blend with the currently processing image.
- /// The source for the current processor instance.
- /// The source area to process for the current processor instance.
- /// The location to draw the blended image.
+ /// The foreground to blend with the currently processing image.
+ /// The source for the current processor instance.
+ /// The location to draw the blended image.
+ /// The source area to process for the current processor instance.
/// The blending mode to use when drawing the image.
- /// The Alpha blending mode to use when drawing the image.
+ /// The alpha blending mode to use when drawing the image.
/// The opacity of the image to blend. Must be between 0 and 1.
public DrawImageProcessor(
Configuration configuration,
- Image image,
- Image source,
- Rectangle sourceRectangle,
- Point location,
+ Image foregroundImage,
+ Image backgroundImage,
+ Point backgroundLocation,
+ Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
- : base(configuration, source, sourceRectangle)
+ : base(configuration, backgroundImage, backgroundImage.Bounds)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
- this.Image = image;
+ this.ForegroundImage = foregroundImage;
+ this.ForegroundRectangle = foregroundRectangle;
this.Opacity = opacity;
this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
- this.Location = location;
+ this.BackgroundLocation = backgroundLocation;
}
///
/// Gets the image to blend
///
- public Image Image { get; }
+ public Image ForegroundImage { get; }
+
+ ///
+ /// Gets the rectangular portion of the foreground image to draw.
+ ///
+ public Rectangle ForegroundRectangle { get; }
///
/// Gets the opacity of the image to blend
@@ -65,43 +71,57 @@ internal class DrawImageProcessor : ImageProcessor
///
/// Gets the location to draw the blended image
///
- public Point Location { get; }
+ public Point BackgroundLocation { get; }
///
protected override void OnFrameApply(ImageFrame source)
{
- Rectangle sourceRectangle = this.SourceRectangle;
- Configuration configuration = this.Configuration;
-
- Image targetImage = this.Image;
- PixelBlender blender = this.Blender;
- int locationY = this.Location.Y;
+ // Align the bounds so that both the source and targets are the same width and height for blending.
+ // We ensure that negative locations are subtracted from both bounds so that foreground images can partially overlap.
+ Rectangle foregroundRectangle = this.ForegroundRectangle;
- // Align start/end positions.
- Rectangle bounds = targetImage.Bounds;
+ // Sanitize the location so that we don't try and sample outside the image.
+ int left = this.BackgroundLocation.X;
+ int top = this.BackgroundLocation.Y;
- int minX = Math.Max(this.Location.X, sourceRectangle.X);
- int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right);
- int targetX = minX - this.Location.X;
-
- int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
- int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
-
- int width = maxX - minX;
+ if (this.BackgroundLocation.X < 0)
+ {
+ foregroundRectangle.Width += this.BackgroundLocation.X;
+ left = 0;
+ }
- Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
+ if (this.BackgroundLocation.Y < 0)
+ {
+ foregroundRectangle.Height += this.BackgroundLocation.Y;
+ top = 0;
+ }
- // Not a valid operation because rectangle does not overlap with this image.
- if (workingRect.Width <= 0 || workingRect.Height <= 0)
+ int width = foregroundRectangle.Width;
+ int height = foregroundRectangle.Height;
+ if (width <= 0 || height <= 0)
{
- throw new ImageProcessingException(
- "Cannot draw image because the source image does not overlap the target image.");
+ // Nothing to do, return.
+ return;
}
- DrawImageProcessor.RowOperation operation = new(source.PixelBuffer, targetImage.Frames.RootFrame.PixelBuffer, blender, configuration, minX, width, locationY, targetX, this.Opacity);
+ // Sanitize the dimensions so that we don't try and sample outside the image.
+ foregroundRectangle = Rectangle.Intersect(foregroundRectangle, this.ForegroundImage.Bounds);
+ Rectangle backgroundRectangle = Rectangle.Intersect(new(left, top, width, height), this.SourceRectangle);
+ Configuration configuration = this.Configuration;
+
+ DrawImageProcessor.RowOperation operation =
+ new(
+ configuration,
+ source.PixelBuffer,
+ this.ForegroundImage.Frames.RootFrame.PixelBuffer,
+ backgroundRectangle,
+ foregroundRectangle,
+ this.Blender,
+ this.Opacity);
+
ParallelRowIterator.IterateRows(
configuration,
- workingRect,
+ new(0, 0, foregroundRectangle.Width, foregroundRectangle.Height),
in operation);
}
@@ -110,36 +130,30 @@ internal class DrawImageProcessor : ImageProcessor
///
private readonly struct RowOperation : IRowOperation
{
- private readonly Buffer2D source;
- private readonly Buffer2D target;
+ private readonly Buffer2D background;
+ private readonly Buffer2D foreground;
private readonly PixelBlender blender;
private readonly Configuration configuration;
- private readonly int minX;
- private readonly int width;
- private readonly int locationY;
- private readonly int targetX;
+ private readonly Rectangle foregroundRectangle;
+ private readonly Rectangle backgroundRectangle;
private readonly float opacity;
[MethodImpl(InliningOptions.ShortMethod)]
public RowOperation(
- Buffer2D source,
- Buffer2D target,
- PixelBlender blender,
Configuration configuration,
- int minX,
- int width,
- int locationY,
- int targetX,
+ Buffer2D background,
+ Buffer2D foreground,
+ Rectangle backgroundRectangle,
+ Rectangle foregroundRectangle,
+ PixelBlender blender,
float opacity)
{
- this.source = source;
- this.target = target;
- this.blender = blender;
this.configuration = configuration;
- this.minX = minX;
- this.width = width;
- this.locationY = locationY;
- this.targetX = targetX;
+ this.background = background;
+ this.foreground = foreground;
+ this.backgroundRectangle = backgroundRectangle;
+ this.foregroundRectangle = foregroundRectangle;
+ this.blender = blender;
this.opacity = opacity;
}
@@ -147,8 +161,8 @@ internal class DrawImageProcessor : ImageProcessor
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{
- Span background = this.source.DangerousGetRowSpan(y).Slice(this.minX, this.width);
- Span foreground = this.target.DangerousGetRowSpan(y - this.locationY).Slice(this.targetX, this.width);
+ Span background = this.background.DangerousGetRowSpan(y + this.backgroundRectangle.Top).Slice(this.backgroundRectangle.Left, this.backgroundRectangle.Width);
+ Span foreground = this.foreground.DangerousGetRowSpan(y + this.foregroundRectangle.Top).Slice(this.foregroundRectangle.Left, this.foregroundRectangle.Width);
this.blender.Blend(this.configuration, background, background, foreground, this.opacity);
}
}
diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs
index 26129c599..59e1bc4d8 100644
--- a/tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs
@@ -13,11 +13,12 @@ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
[Fact]
public void DrawImage_OpacityOnly_VerifyGraphicOptionsTakenFromContext()
{
- // non-default values as we cant easly defect usage otherwise
+ // non-default values as we cant easily defect usage otherwise
this.options.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
- this.operations.DrawImage(null, 0.5f);
+ using Image image = new(Configuration.Default, 1, 1);
+ this.operations.DrawImage(image, 0.5f);
DrawImageProcessor dip = this.Verify();
Assert.Equal(0.5, dip.Opacity);
@@ -28,11 +29,12 @@ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
[Fact]
public void DrawImage_OpacityAndBlending_VerifyGraphicOptionsTakenFromContext()
{
- // non-default values as we cant easly defect usage otherwise
+ // non-default values as we cant easily defect usage otherwise
this.options.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
- this.operations.DrawImage(null, PixelColorBlendingMode.Multiply, 0.5f);
+ using Image image = new(Configuration.Default, 1, 1);
+ this.operations.DrawImage(image, PixelColorBlendingMode.Multiply, 0.5f);
DrawImageProcessor dip = this.Verify();
Assert.Equal(0.5, dip.Opacity);
@@ -43,11 +45,12 @@ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
[Fact]
public void DrawImage_LocationAndOpacity_VerifyGraphicOptionsTakenFromContext()
{
- // non-default values as we cant easly defect usage otherwise
+ // non-default values as we cant easily defect usage otherwise
this.options.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
- this.operations.DrawImage(null, Point.Empty, 0.5f);
+ using Image image = new(Configuration.Default, 1, 1);
+ this.operations.DrawImage(image, Point.Empty, 0.5f);
DrawImageProcessor dip = this.Verify();
Assert.Equal(0.5, dip.Opacity);
@@ -58,11 +61,12 @@ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
[Fact]
public void DrawImage_LocationAndOpacityAndBlending_VerifyGraphicOptionsTakenFromContext()
{
- // non-default values as we cant easly defect usage otherwise
+ // non-default values as we cant easily defect usage otherwise
this.options.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
- this.operations.DrawImage(null, Point.Empty, PixelColorBlendingMode.Multiply, 0.5f);
+ using Image image = new(Configuration.Default, 1, 1);
+ this.operations.DrawImage(image, Point.Empty, PixelColorBlendingMode.Multiply, 0.5f);
DrawImageProcessor dip = this.Verify();
Assert.Equal(0.5, dip.Opacity);
diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
index d017e5ad4..8b0db773a 100644
--- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
@@ -190,18 +190,65 @@ public class DrawImageTests
}
[Theory]
- [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, -30)]
- [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, -30)]
- [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, 130)]
- [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, 130)]
- public void NonOverlappingImageThrows(TestImageProvider provider, int x, int y)
+ [WithFile(TestImages.Png.Issue2447, PixelTypes.Rgba32)]
+ public void Issue2447_A(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
{
- using Image background = provider.GetImage();
- using Image overlay = new(Configuration.Default, 10, 10, Color.Black);
- ImageProcessingException ex = Assert.Throws(Test);
+ using Image foreground = provider.GetImage();
+ using Image background = new(100, 100, new Rgba32(0, 255, 255));
- Assert.Contains("does not overlap", ex.ToString());
+ background.Mutate(c => c.DrawImage(foreground, new Point(64, 10), new Rectangle(32, 32, 32, 32), 1F));
- void Test() => background.Mutate(context => context.DrawImage(overlay, new Point(x, y), new GraphicsOptions()));
+ background.DebugSave(
+ provider,
+ appendPixelTypeToFileName: false,
+ appendSourceFileOrDescription: false);
+
+ background.CompareToReferenceOutput(
+ provider,
+ appendPixelTypeToFileName: false,
+ appendSourceFileOrDescription: false);
+ }
+
+ [Theory]
+ [WithFile(TestImages.Png.Issue2447, PixelTypes.Rgba32)]
+ public void Issue2447_B(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image foreground = provider.GetImage();
+ using Image background = new(100, 100, new Rgba32(0, 255, 255));
+
+ background.Mutate(c => c.DrawImage(foreground, new Point(10, 10), new Rectangle(320, 128, 32, 32), 1F));
+
+ background.DebugSave(
+ provider,
+ appendPixelTypeToFileName: false,
+ appendSourceFileOrDescription: false);
+
+ background.CompareToReferenceOutput(
+ provider,
+ appendPixelTypeToFileName: false,
+ appendSourceFileOrDescription: false);
+ }
+
+ [Theory]
+ [WithFile(TestImages.Png.Issue2447, PixelTypes.Rgba32)]
+ public void Issue2447_C(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image foreground = provider.GetImage();
+ using Image background = new(100, 100, new Rgba32(0, 255, 255));
+
+ background.Mutate(c => c.DrawImage(foreground, new Point(10, 10), new Rectangle(32, 32, 32, 32), 1F));
+
+ background.DebugSave(
+ provider,
+ appendPixelTypeToFileName: false,
+ appendSourceFileOrDescription: false);
+
+ background.CompareToReferenceOutput(
+ provider,
+ appendPixelTypeToFileName: false,
+ appendSourceFileOrDescription: false);
}
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 1b0c55973..39b8c95a9 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -135,6 +135,9 @@ public static class TestImages
// Issue 2259: https://github.com/SixLabors/ImageSharp/issues/2469
public const string Issue2469 = "Png/issues/issue_2469.png";
+ // Issue 2447: https://github.com/SixLabors/ImageSharp/issues/2447
+ public const string Issue2447 = "Png/issues/issue_2447.png";
+
public static class Bad
{
public const string MissingDataChunk = "Png/xdtn0g01.png";
diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_A.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_A.png
new file mode 100644
index 000000000..6bf7bb19b
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_A.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2012789669110c08a00d37add7f53967b902bd617c90f85d7e90b13a32a0a429
+size 354
diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_B.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_B.png
new file mode 100644
index 000000000..232184c4c
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_B.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f628327efbf1e530d32dc092f2ab361de5ab35fe78db6b5e0274c71f1d170496
+size 363
diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_C.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_C.png
new file mode 100644
index 000000000..fe4e44fbf
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_C.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:40fc8f14b8f9e98fd73855f3dfada39062cc1aff874b3389133a55eb2e968f66
+size 354
diff --git a/tests/Images/Input/Png/issues/issue_2447.png b/tests/Images/Input/Png/issues/issue_2447.png
new file mode 100644
index 000000000..3b79487c5
--- /dev/null
+++ b/tests/Images/Input/Png/issues/issue_2447.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:52f7e55f812db926d95ac1ab0c3235fbaca53331b99f73e65f3c1c2094503e20
+size 15824