Browse Source

Merge branch 'main' into js/2469-color-LUT-memory

pull/2473/head
James Jackson-South 3 years ago
committed by GitHub
parent
commit
d3e88332d6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 162
      src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
  2. 59
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
  3. 132
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  4. 20
      tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs
  5. 67
      tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
  6. 3
      tests/ImageSharp.Tests/TestImages.cs
  7. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_A.png
  8. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_B.png
  9. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/Issue2447_C.png
  10. 3
      tests/Images/Input/Png/issues/issue_2447.png

162
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. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
float opacity) float opacity)
{ {
GraphicsOptions options = source.GetGraphicsOptions(); GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, image, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
} }
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="foregroundRectangle">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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Rectangle rectangle, Rectangle foregroundRectangle,
float opacity) float opacity)
{ {
GraphicsOptions options = source.GetGraphicsOptions(); GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, image, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
} }
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">The image to draw on the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
PixelColorBlendingMode colorBlending, PixelColorBlendingMode colorBlending,
float opacity) float opacity)
=> DrawImage(source, image, Point.Empty, colorBlending, opacity); => DrawImage(source, foreground, Point.Empty, colorBlending, opacity);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="foregroundRectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="colorBlending">The color blending mode.</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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Rectangle rectangle, Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending, PixelColorBlendingMode colorBlending,
float opacity) float opacity)
=> DrawImage(source, image, rectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); => DrawImage(source, foreground, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">The image to draw on the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param> /// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition 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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
PixelColorBlendingMode colorBlending, PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition, PixelAlphaCompositionMode alphaComposition,
float opacity) float opacity)
=> DrawImage(source, image, Point.Empty, colorBlending, alphaComposition, opacity); => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="foregroundRectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="colorBlending">The color blending mode.</param> /// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition 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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Rectangle rectangle, Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending, PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition, PixelAlphaCompositionMode alphaComposition,
float opacity) float opacity)
=> DrawImage(source, image, Point.Empty, rectangle, colorBlending, alphaComposition, opacity); => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">The image to draw on the currently processing image.</param>
/// <param name="options">The options, including the blending type and blending amount.</param> /// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
GraphicsOptions options) GraphicsOptions options)
=> DrawImage(source, image, Point.Empty, options); => DrawImage(source, foreground, Point.Empty, options);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="foregroundRectangle">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> /// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Rectangle rectangle, Rectangle foregroundRectangle,
GraphicsOptions options) GraphicsOptions options)
=> DrawImage(source, image, Point.Empty, rectangle, options); => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="backgroundLocation">The location on the currently processing image at which to draw.</param>
/// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Point location, Point backgroundLocation,
float opacity) float opacity)
{ {
GraphicsOptions options = source.GetGraphicsOptions(); GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, image, location, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
} }
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="backgroundLocation">The location on the currently 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="foregroundRectangle">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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Point location, Point backgroundLocation,
Rectangle rectangle, Rectangle foregroundRectangle,
float opacity) float opacity)
{ {
GraphicsOptions options = source.GetGraphicsOptions(); 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);
} }
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="backgroundLocation">The location on the currently processing image at which to draw.</param>
/// <param name="colorBlending">The color blending to apply.</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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Point location, Point backgroundLocation,
PixelColorBlendingMode colorBlending, PixelColorBlendingMode colorBlending,
float opacity) float opacity)
=> DrawImage(source, image, location, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="backgroundLocation">The location on the currently 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="foregroundRectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="colorBlending">The color blending to apply.</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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Point location, Point backgroundLocation,
Rectangle rectangle, Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending, PixelColorBlendingMode colorBlending,
float opacity) float opacity)
=> DrawImage(source, image, location, rectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="backgroundLocation">The location on the currently processing image at which to draw.</param>
/// <param name="options">The options containing the blend mode and opacity.</param> /// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Point location, Point backgroundLocation,
GraphicsOptions options) GraphicsOptions options)
=> DrawImage(source, image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); => DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="backgroundLocation">The location on the currently 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="foregroundRectangle">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> /// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Point location, Point backgroundLocation,
Rectangle rectangle, Rectangle foregroundRectangle,
GraphicsOptions options) GraphicsOptions options)
=> DrawImage(source, image, location, rectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="backgroundLocation">The location on the currently processing image at which to draw.</param>
/// <param name="colorBlending">The color blending to apply.</param> /// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition 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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Point location, Point backgroundLocation,
PixelColorBlendingMode colorBlending, PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition, PixelAlphaCompositionMode alphaComposition,
float opacity) float opacity)
=> source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity)); => source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the currently processing image by blending their pixels. /// Draws the given image together with the currently processing image by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The current image processing context.</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="foreground">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="backgroundLocation">The location on the currently 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="foregroundRectangle">The rectangle structure that specifies the portion of the image to draw.</param>
/// <param name="colorBlending">The color blending to apply.</param> /// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition 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> /// <param name="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns> /// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image foreground,
Point location, Point backgroundLocation,
Rectangle rectangle, Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending, PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition, PixelAlphaCompositionMode alphaComposition,
float opacity) => float opacity) =>
source.ApplyProcessor( source.ApplyProcessor(
new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity), new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity),
rectangle); foregroundRectangle);
} }

59
src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -14,20 +14,41 @@ public class DrawImageProcessor : IImageProcessor
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor"/> class. /// Initializes a new instance of the <see cref="DrawImageProcessor"/> class.
/// </summary> /// </summary>
/// <param name="image">The image to blend.</param> /// <param name="foreground">The image to blend.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="backgroundLocation">The location to draw the foreground image on the background.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param> /// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param> /// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend.</param> /// <param name="opacity">The opacity of the image to blend.</param>
public DrawImageProcessor( public DrawImageProcessor(
Image image, Image foreground,
Point location, Point backgroundLocation,
PixelColorBlendingMode colorBlendingMode, PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode, PixelAlphaCompositionMode alphaCompositionMode,
float opacity) float opacity)
: this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity)
{ {
this.Image = image; }
this.Location = location;
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor"/> class.
/// </summary>
/// <param name="foreground">The image to blend.</param>
/// <param name="backgroundLocation">The location to draw the foreground image on the background.</param>
/// <param name="foregroundRectangle">The rectangular portion of the foreground image to draw.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend.</param>
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.ColorBlendingMode = colorBlendingMode;
this.AlphaCompositionMode = alphaCompositionMode; this.AlphaCompositionMode = alphaCompositionMode;
this.Opacity = opacity; this.Opacity = opacity;
@ -36,12 +57,17 @@ public class DrawImageProcessor : IImageProcessor
/// <summary> /// <summary>
/// Gets the image to blend. /// Gets the image to blend.
/// </summary> /// </summary>
public Image Image { get; } public Image ForeGround { get; }
/// <summary>
/// Gets the location to draw the foreground image on the background.
/// </summary>
public Point BackgroundLocation { get; }
/// <summary> /// <summary>
/// Gets the location to draw the blended image. /// Gets the rectangular portion of the foreground image to draw.
/// </summary> /// </summary>
public Point Location { get; } public Rectangle ForegroundRectangle { get; }
/// <summary> /// <summary>
/// Gets the blending mode to use when drawing the image. /// Gets the blending mode to use when drawing the image.
@ -62,8 +88,8 @@ public class DrawImageProcessor : IImageProcessor
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>(Configuration configuration, Image<TPixelBg> source, Rectangle sourceRectangle) public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>(Configuration configuration, Image<TPixelBg> source, Rectangle sourceRectangle)
where TPixelBg : unmanaged, IPixel<TPixelBg> where TPixelBg : unmanaged, IPixel<TPixelBg>
{ {
ProcessorFactoryVisitor<TPixelBg> visitor = new(configuration, this, source, sourceRectangle); ProcessorFactoryVisitor<TPixelBg> visitor = new(configuration, this, source);
this.Image.AcceptVisitor(visitor); this.ForeGround.AcceptVisitor(visitor);
return visitor.Result!; return visitor.Result!;
} }
@ -73,14 +99,15 @@ public class DrawImageProcessor : IImageProcessor
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly DrawImageProcessor definition; private readonly DrawImageProcessor definition;
private readonly Image<TPixelBg> source; private readonly Image<TPixelBg> source;
private readonly Rectangle sourceRectangle;
public ProcessorFactoryVisitor(Configuration configuration, DrawImageProcessor definition, Image<TPixelBg> source, Rectangle sourceRectangle) public ProcessorFactoryVisitor(
Configuration configuration,
DrawImageProcessor definition,
Image<TPixelBg> source)
{ {
this.configuration = configuration; this.configuration = configuration;
this.definition = definition; this.definition = definition;
this.source = source; this.source = source;
this.sourceRectangle = sourceRectangle;
} }
public IImageProcessor<TPixelBg>? Result { get; private set; } public IImageProcessor<TPixelBg>? Result { get; private set; }
@ -91,8 +118,8 @@ public class DrawImageProcessor : IImageProcessor
this.configuration, this.configuration,
image, image,
this.source, this.source,
this.sourceRectangle, this.definition.BackgroundLocation,
this.definition.Location, this.definition.ForegroundRectangle,
this.definition.ColorBlendingMode, this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode, this.definition.AlphaCompositionMode,
this.definition.Opacity); this.definition.Opacity);

132
src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

@ -21,36 +21,42 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelBg, TPixelFg}"/> class. /// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelBg, TPixelFg}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="image">The foreground <see cref="Image{TPixelFg}"/> to blend with the currently processing image.</param> /// <param name="foregroundImage">The foreground <see cref="Image{TPixelFg}"/> to blend with the currently processing image.</param>
/// <param name="source">The source <see cref="Image{TPixelBg}"/> for the current processor instance.</param> /// <param name="backgroundImage">The source <see cref="Image{TPixelBg}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param> /// <param name="backgroundLocation">The location to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="foregroundRectangle">The source area to process for the current processor instance.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param> /// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param> /// <param name="alphaCompositionMode">The alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor( public DrawImageProcessor(
Configuration configuration, Configuration configuration,
Image<TPixelFg> image, Image<TPixelFg> foregroundImage,
Image<TPixelBg> source, Image<TPixelBg> backgroundImage,
Rectangle sourceRectangle, Point backgroundLocation,
Point location, Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlendingMode, PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode, PixelAlphaCompositionMode alphaCompositionMode,
float opacity) float opacity)
: base(configuration, source, sourceRectangle) : base(configuration, backgroundImage, backgroundImage.Bounds)
{ {
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image; this.ForegroundImage = foregroundImage;
this.ForegroundRectangle = foregroundRectangle;
this.Opacity = opacity; this.Opacity = opacity;
this.Blender = PixelOperations<TPixelBg>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.Blender = PixelOperations<TPixelBg>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location; this.BackgroundLocation = backgroundLocation;
} }
/// <summary> /// <summary>
/// Gets the image to blend /// Gets the image to blend
/// </summary> /// </summary>
public Image<TPixelFg> Image { get; } public Image<TPixelFg> ForegroundImage { get; }
/// <summary>
/// Gets the rectangular portion of the foreground image to draw.
/// </summary>
public Rectangle ForegroundRectangle { get; }
/// <summary> /// <summary>
/// Gets the opacity of the image to blend /// Gets the opacity of the image to blend
@ -65,43 +71,57 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
/// <summary> /// <summary>
/// Gets the location to draw the blended image /// Gets the location to draw the blended image
/// </summary> /// </summary>
public Point Location { get; } public Point BackgroundLocation { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixelBg> source) protected override void OnFrameApply(ImageFrame<TPixelBg> source)
{ {
Rectangle sourceRectangle = this.SourceRectangle; // Align the bounds so that both the source and targets are the same width and height for blending.
Configuration configuration = this.Configuration; // We ensure that negative locations are subtracted from both bounds so that foreground images can partially overlap.
Rectangle foregroundRectangle = this.ForegroundRectangle;
Image<TPixelFg> targetImage = this.Image;
PixelBlender<TPixelBg> blender = this.Blender;
int locationY = this.Location.Y;
// Align start/end positions. // Sanitize the location so that we don't try and sample outside the image.
Rectangle bounds = targetImage.Bounds; int left = this.BackgroundLocation.X;
int top = this.BackgroundLocation.Y;
int minX = Math.Max(this.Location.X, sourceRectangle.X); if (this.BackgroundLocation.X < 0)
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right); {
int targetX = minX - this.Location.X; foregroundRectangle.Width += this.BackgroundLocation.X;
left = 0;
int minY = Math.Max(this.Location.Y, sourceRectangle.Y); }
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
int width = maxX - minX;
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. int width = foregroundRectangle.Width;
if (workingRect.Width <= 0 || workingRect.Height <= 0) int height = foregroundRectangle.Height;
if (width <= 0 || height <= 0)
{ {
throw new ImageProcessingException( // Nothing to do, return.
"Cannot draw image because the source image does not overlap the target image."); return;
} }
DrawImageProcessor<TPixelBg, TPixelFg>.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<TPixelBg, TPixelFg>.RowOperation operation =
new(
configuration,
source.PixelBuffer,
this.ForegroundImage.Frames.RootFrame.PixelBuffer,
backgroundRectangle,
foregroundRectangle,
this.Blender,
this.Opacity);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
configuration, configuration,
workingRect, new(0, 0, foregroundRectangle.Width, foregroundRectangle.Height),
in operation); in operation);
} }
@ -110,36 +130,30 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
/// </summary> /// </summary>
private readonly struct RowOperation : IRowOperation private readonly struct RowOperation : IRowOperation
{ {
private readonly Buffer2D<TPixelBg> source; private readonly Buffer2D<TPixelBg> background;
private readonly Buffer2D<TPixelFg> target; private readonly Buffer2D<TPixelFg> foreground;
private readonly PixelBlender<TPixelBg> blender; private readonly PixelBlender<TPixelBg> blender;
private readonly Configuration configuration; private readonly Configuration configuration;
private readonly int minX; private readonly Rectangle foregroundRectangle;
private readonly int width; private readonly Rectangle backgroundRectangle;
private readonly int locationY;
private readonly int targetX;
private readonly float opacity; private readonly float opacity;
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public RowOperation( public RowOperation(
Buffer2D<TPixelBg> source,
Buffer2D<TPixelFg> target,
PixelBlender<TPixelBg> blender,
Configuration configuration, Configuration configuration,
int minX, Buffer2D<TPixelBg> background,
int width, Buffer2D<TPixelFg> foreground,
int locationY, Rectangle backgroundRectangle,
int targetX, Rectangle foregroundRectangle,
PixelBlender<TPixelBg> blender,
float opacity) float opacity)
{ {
this.source = source;
this.target = target;
this.blender = blender;
this.configuration = configuration; this.configuration = configuration;
this.minX = minX; this.background = background;
this.width = width; this.foreground = foreground;
this.locationY = locationY; this.backgroundRectangle = backgroundRectangle;
this.targetX = targetX; this.foregroundRectangle = foregroundRectangle;
this.blender = blender;
this.opacity = opacity; this.opacity = opacity;
} }
@ -147,8 +161,8 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y) public void Invoke(int y)
{ {
Span<TPixelBg> background = this.source.DangerousGetRowSpan(y).Slice(this.minX, this.width); Span<TPixelBg> background = this.background.DangerousGetRowSpan(y + this.backgroundRectangle.Top).Slice(this.backgroundRectangle.Left, this.backgroundRectangle.Width);
Span<TPixelFg> foreground = this.target.DangerousGetRowSpan(y - this.locationY).Slice(this.targetX, this.width); Span<TPixelFg> foreground = this.foreground.DangerousGetRowSpan(y + this.foregroundRectangle.Top).Slice(this.foregroundRectangle.Left, this.foregroundRectangle.Width);
this.blender.Blend<TPixelFg>(this.configuration, background, background, foreground, this.opacity); this.blender.Blend<TPixelFg>(this.configuration, background, background, foreground, this.opacity);
} }
} }

20
tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs

@ -13,11 +13,12 @@ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
[Fact] [Fact]
public void DrawImage_OpacityOnly_VerifyGraphicOptionsTakenFromContext() 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.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen; this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
this.operations.DrawImage(null, 0.5f); using Image<Rgba32> image = new(Configuration.Default, 1, 1);
this.operations.DrawImage(image, 0.5f);
DrawImageProcessor dip = this.Verify<DrawImageProcessor>(); DrawImageProcessor dip = this.Verify<DrawImageProcessor>();
Assert.Equal(0.5, dip.Opacity); Assert.Equal(0.5, dip.Opacity);
@ -28,11 +29,12 @@ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
[Fact] [Fact]
public void DrawImage_OpacityAndBlending_VerifyGraphicOptionsTakenFromContext() 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.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen; this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
this.operations.DrawImage(null, PixelColorBlendingMode.Multiply, 0.5f); using Image<Rgba32> image = new(Configuration.Default, 1, 1);
this.operations.DrawImage(image, PixelColorBlendingMode.Multiply, 0.5f);
DrawImageProcessor dip = this.Verify<DrawImageProcessor>(); DrawImageProcessor dip = this.Verify<DrawImageProcessor>();
Assert.Equal(0.5, dip.Opacity); Assert.Equal(0.5, dip.Opacity);
@ -43,11 +45,12 @@ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
[Fact] [Fact]
public void DrawImage_LocationAndOpacity_VerifyGraphicOptionsTakenFromContext() 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.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen; this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
this.operations.DrawImage(null, Point.Empty, 0.5f); using Image<Rgba32> image = new(Configuration.Default, 1, 1);
this.operations.DrawImage(image, Point.Empty, 0.5f);
DrawImageProcessor dip = this.Verify<DrawImageProcessor>(); DrawImageProcessor dip = this.Verify<DrawImageProcessor>();
Assert.Equal(0.5, dip.Opacity); Assert.Equal(0.5, dip.Opacity);
@ -58,11 +61,12 @@ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
[Fact] [Fact]
public void DrawImage_LocationAndOpacityAndBlending_VerifyGraphicOptionsTakenFromContext() 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.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
this.options.ColorBlendingMode = PixelColorBlendingMode.Screen; this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
this.operations.DrawImage(null, Point.Empty, PixelColorBlendingMode.Multiply, 0.5f); using Image<Rgba32> image = new(Configuration.Default, 1, 1);
this.operations.DrawImage(image, Point.Empty, PixelColorBlendingMode.Multiply, 0.5f);
DrawImageProcessor dip = this.Verify<DrawImageProcessor>(); DrawImageProcessor dip = this.Verify<DrawImageProcessor>();
Assert.Equal(0.5, dip.Opacity); Assert.Equal(0.5, dip.Opacity);

67
tests/ImageSharp.Tests/Drawing/DrawImageTests.cs

@ -190,18 +190,65 @@ public class DrawImageTests
} }
[Theory] [Theory]
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, -30)] [WithFile(TestImages.Png.Issue2447, PixelTypes.Rgba32)]
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, -30)] public void Issue2447_A<TPixel>(TestImageProvider<TPixel> provider)
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, 130)] where TPixel : unmanaged, IPixel<TPixel>
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, 130)]
public void NonOverlappingImageThrows(TestImageProvider<Rgba32> provider, int x, int y)
{ {
using Image<Rgba32> background = provider.GetImage(); using Image<TPixel> foreground = provider.GetImage();
using Image<Rgba32> overlay = new(Configuration.Default, 10, 10, Color.Black); using Image<Rgba32> background = new(100, 100, new Rgba32(0, 255, 255));
ImageProcessingException ex = Assert.Throws<ImageProcessingException>(Test);
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> foreground = provider.GetImage();
using Image<Rgba32> 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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> foreground = provider.GetImage();
using Image<Rgba32> 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);
} }
} }

3
tests/ImageSharp.Tests/TestImages.cs

@ -135,6 +135,9 @@ public static class TestImages
// Issue 2259: https://github.com/SixLabors/ImageSharp/issues/2469 // Issue 2259: https://github.com/SixLabors/ImageSharp/issues/2469
public const string Issue2469 = "Png/issues/issue_2469.png"; 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 static class Bad
{ {
public const string MissingDataChunk = "Png/xdtn0g01.png"; public const string MissingDataChunk = "Png/xdtn0g01.png";

3
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

3
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

3
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

3
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
Loading…
Cancel
Save