Browse Source

Merge pull request #2995 from Yushu2606/patch

feat: Add animation loop control to DrawImage with repeatCount parameter
pull/3055/head
James Jackson-South 3 months ago
committed by GitHub
parent
commit
9a0b9466bd
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 378
      src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
  2. 19
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs
  3. 33
      src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  4. 17
      tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
  5. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/00.png
  6. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/01.png
  7. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/02.png
  8. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/03.png
  9. 3
      tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/04.png

378
src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs

@ -22,9 +22,27 @@ public static class DrawImageExtensions
this IImageProcessingContext source,
Image foreground,
float opacity)
=> DrawImage(source, foreground, opacity, 0);
/// <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="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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
float opacity,
int foregroundRepeatCount)
{
GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount);
}
/// <summary>
@ -40,9 +58,29 @@ public static class DrawImageExtensions
Image foreground,
Rectangle foregroundRectangle,
float opacity)
=> DrawImage(source, foreground, foregroundRectangle, opacity, 0);
/// <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="foreground">The image to draw on the currently processing image.</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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Rectangle foregroundRectangle,
float opacity,
int foregroundRepeatCount)
{
GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount);
}
/// <summary>
@ -58,7 +96,27 @@ public static class DrawImageExtensions
Image foreground,
PixelColorBlendingMode colorBlending,
float opacity)
=> DrawImage(source, foreground, Point.Empty, colorBlending, opacity);
=> DrawImage(source, foreground, colorBlending, opacity, 0);
/// <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="foreground">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>
/// <param name="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
PixelColorBlendingMode colorBlending,
float opacity,
int foregroundRepeatCount)
=> DrawImage(source, foreground, Point.Empty, colorBlending, opacity, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -75,7 +133,29 @@ public static class DrawImageExtensions
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
float opacity)
=> DrawImage(source, foreground, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
=> DrawImage(source, foreground, foregroundRectangle, colorBlending, opacity, 0);
/// <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="foreground">The image to draw on the currently processing image.</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="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <param name="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
float opacity,
int foregroundRepeatCount)
=> DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, opacity, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -92,7 +172,29 @@ public static class DrawImageExtensions
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity)
=> DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity);
=> DrawImage(source, foreground, colorBlending, alphaComposition, opacity, 0);
/// <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="foreground">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 draw. Must be between 0 and 1.</param>
/// <param name="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity,
int foregroundRepeatCount)
=> DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -111,7 +213,31 @@ public static class DrawImageExtensions
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity)
=> DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity);
=> DrawImage(source, foreground, foregroundRectangle, colorBlending, alphaComposition, opacity, 0);
/// <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="foreground">The image to draw on the currently processing image.</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="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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity,
int foregroundRepeatCount)
=> DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -124,7 +250,25 @@ public static class DrawImageExtensions
this IImageProcessingContext source,
Image foreground,
GraphicsOptions options)
=> DrawImage(source, foreground, Point.Empty, options);
=> DrawImage(source, foreground, options, 0);
/// <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="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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
GraphicsOptions options,
int foregroundRepeatCount)
=> DrawImage(source, foreground, Point.Empty, options, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -139,7 +283,27 @@ public static class DrawImageExtensions
Image foreground,
Rectangle foregroundRectangle,
GraphicsOptions options)
=> DrawImage(source, foreground, Point.Empty, foregroundRectangle, options);
=> DrawImage(source, foreground, foregroundRectangle, options, 0);
/// <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="foreground">The image to draw on the currently processing image.</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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Rectangle foregroundRectangle,
GraphicsOptions options,
int foregroundRepeatCount)
=> DrawImage(source, foreground, Point.Empty, foregroundRectangle, options, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -154,9 +318,29 @@ public static class DrawImageExtensions
Image foreground,
Point backgroundLocation,
float opacity)
=> DrawImage(source, foreground, backgroundLocation, opacity, 0);
/// <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="foreground">The image to draw on the currently processing image.</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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Point backgroundLocation,
float opacity,
int foregroundRepeatCount)
{
GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount);
}
/// <summary>
@ -174,9 +358,31 @@ public static class DrawImageExtensions
Point backgroundLocation,
Rectangle foregroundRectangle,
float opacity)
=> DrawImage(source, foreground, backgroundLocation, foregroundRectangle, opacity, 0);
/// <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="foreground">The image to draw on the currently processing image.</param>
/// <param name="backgroundLocation">The location on the currently processing image at which 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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Point backgroundLocation,
Rectangle foregroundRectangle,
float opacity,
int foregroundRepeatCount)
{
GraphicsOptions options = source.GetGraphicsOptions();
return DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity);
return DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount);
}
/// <summary>
@ -194,7 +400,29 @@ public static class DrawImageExtensions
Point backgroundLocation,
PixelColorBlendingMode colorBlending,
float opacity)
=> DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
=> DrawImage(source, foreground, backgroundLocation, colorBlending, opacity, 0);
/// <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="foreground">The image to draw on the currently processing image.</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="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <param name="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Point backgroundLocation,
PixelColorBlendingMode colorBlending,
float opacity,
int foregroundRepeatCount)
=> DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -213,7 +441,31 @@ public static class DrawImageExtensions
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
float opacity)
=> DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity);
=> DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, opacity, 0);
/// <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="foreground">The image to draw on the currently processing image.</param>
/// <param name="backgroundLocation">The location on the currently processing image at which 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="opacity">The opacity of the image to draw. Must be between 0 and 1.</param>
/// <param name="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Point backgroundLocation,
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
float opacity,
int foregroundRepeatCount)
=> DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -228,7 +480,27 @@ public static class DrawImageExtensions
Image foreground,
Point backgroundLocation,
GraphicsOptions options)
=> DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
=> DrawImage(source, foreground, backgroundLocation, options, 0);
/// <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="foreground">The image to draw on the currently processing image.</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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Point backgroundLocation,
GraphicsOptions options,
int foregroundRepeatCount)
=> DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage, foregroundRepeatCount);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -245,7 +517,48 @@ public static class DrawImageExtensions
Point backgroundLocation,
Rectangle foregroundRectangle,
GraphicsOptions options)
=> DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage);
=> DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options, 0);
/// <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="foreground">The image to draw on the currently processing image.</param>
/// <param name="backgroundLocation">The location on the currently processing image at which 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="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image foreground,
Point backgroundLocation,
Rectangle foregroundRectangle,
GraphicsOptions options,
int foregroundRepeatCount)
=> DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage, foregroundRepeatCount);
/// <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="foreground">The image to draw on the currently processing image.</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="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 foreground,
Point backgroundLocation,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity)
=> DrawImage(source, foreground, backgroundLocation, colorBlending, alphaComposition, opacity, 0);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -256,6 +569,10 @@ public static class DrawImageExtensions
/// <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>
/// <param name="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
@ -263,8 +580,30 @@ public static class DrawImageExtensions
Point backgroundLocation,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity,
int foregroundRepeatCount)
=> source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity, foregroundRepeatCount));
/// <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="foreground">The image to draw on the currently processing image.</param>
/// <param name="backgroundLocation">The location on the currently processing image at which 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="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 foreground,
Point backgroundLocation,
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity)
=> source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity));
=> DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity, 0);
/// <summary>
/// Draws the given image together with the currently processing image by blending their pixels.
@ -276,6 +615,10 @@ public static class DrawImageExtensions
/// <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>
/// <param name="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this operation across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
@ -284,8 +627,9 @@ public static class DrawImageExtensions
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
float opacity,
int foregroundRepeatCount) =>
source.ApplyProcessor(
new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity),
new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity, foregroundRepeatCount),
foregroundRectangle);
}

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

@ -19,13 +19,15 @@ public class DrawImageProcessor : IImageProcessor
/// <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>
/// <param name="foregroundRepeatCount">The number of times the foreground frames are allowed to loop. 0 means infinitely.</param>
public DrawImageProcessor(
Image foreground,
Point backgroundLocation,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
: this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity)
float opacity,
int foregroundRepeatCount)
: this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity, foregroundRepeatCount)
{
}
@ -38,13 +40,15 @@ public class DrawImageProcessor : IImageProcessor
/// <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>
/// <param name="foregroundRepeatCount">The number of times the foreground frames are allowed to loop. 0 means infinitely.</param>
public DrawImageProcessor(
Image foreground,
Point backgroundLocation,
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
float opacity,
int foregroundRepeatCount)
{
this.ForeGround = foreground;
this.BackgroundLocation = backgroundLocation;
@ -52,6 +56,7 @@ public class DrawImageProcessor : IImageProcessor
this.ColorBlendingMode = colorBlendingMode;
this.AlphaCompositionMode = alphaCompositionMode;
this.Opacity = opacity;
this.ForegroundRepeatCount = foregroundRepeatCount;
}
/// <summary>
@ -84,6 +89,11 @@ public class DrawImageProcessor : IImageProcessor
/// </summary>
public float Opacity { get; }
/// <summary>
/// Gets the number of times the foreground frames are allowed to loop. 0 means infinitely.
/// </summary>
public int ForegroundRepeatCount { get; }
/// <inheritdoc />
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>(Configuration configuration, Image<TPixelBg> source, Rectangle sourceRectangle)
where TPixelBg : unmanaged, IPixel<TPixelBg>
@ -122,6 +132,7 @@ public class DrawImageProcessor : IImageProcessor
this.definition.ForegroundRectangle,
this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode,
this.definition.Opacity);
this.definition.Opacity,
this.definition.ForegroundRepeatCount);
}
}

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

@ -17,6 +17,12 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
where TPixelBg : unmanaged, IPixel<TPixelBg>
where TPixelFg : unmanaged, IPixel<TPixelFg>
{
/// <summary>
/// Counts how many times <see cref="OnFrameApply"/> has been called for this processor instance.
/// Used to select the current foreground frame.
/// </summary>
private int foregroundFrameCounter;
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelBg, TPixelFg}"/> class.
/// </summary>
@ -28,6 +34,10 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
/// <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. Must be between 0 and 1.</param>
/// <param name="foregroundRepeatCount">
/// The number of times the foreground frames are allowed to loop while applying this processor across successive frames.
/// A value of 0 means loop indefinitely.
/// </param>
public DrawImageProcessor(
Configuration configuration,
Image<TPixelFg> foregroundImage,
@ -36,9 +46,11 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
Rectangle foregroundRectangle,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
float opacity,
int foregroundRepeatCount)
: base(configuration, backgroundImage, backgroundImage.Bounds)
{
Guard.MustBeGreaterThanOrEqualTo(foregroundRepeatCount, 0, nameof(foregroundRepeatCount));
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.ForegroundImage = foregroundImage;
@ -46,6 +58,7 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
this.Opacity = opacity;
this.Blender = PixelOperations<TPixelBg>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.BackgroundLocation = backgroundLocation;
this.ForegroundRepeatCount = foregroundRepeatCount;
}
/// <summary>
@ -73,6 +86,12 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
/// </summary>
public Point BackgroundLocation { get; }
/// <summary>
/// Gets the number of times the foreground frames are allowed to loop while applying this processor across
/// successive frames. A value of 0 means loop indefinitely.
/// </summary>
public int ForegroundRepeatCount { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixelBg> source)
{
@ -114,12 +133,13 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
// Sanitize the dimensions so that we don't try and sample outside the image.
Rectangle backgroundRectangle = Rectangle.Intersect(new Rectangle(left, top, width, height), this.SourceRectangle);
Configuration configuration = this.Configuration;
int currentFrameIndex = this.foregroundFrameCounter % this.ForegroundImage.Frames.Count;
DrawImageProcessor<TPixelBg, TPixelFg>.RowOperation operation =
RowOperation operation =
new(
configuration,
source.PixelBuffer,
this.ForegroundImage.Frames.RootFrame.PixelBuffer,
this.ForegroundImage.Frames[currentFrameIndex].PixelBuffer,
backgroundRectangle,
foregroundRectangle,
this.Blender,
@ -129,6 +149,13 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
configuration,
new Rectangle(0, 0, foregroundRectangle.Width, foregroundRectangle.Height),
in operation);
// The repeat count only affects how the foreground frame advances across successive background frames.
// When exhausted, the selected foreground frame stops advancing.
if (this.ForegroundRepeatCount is 0 || this.foregroundFrameCounter / this.ForegroundImage.Frames.Count < this.ForegroundRepeatCount)
{
this.foregroundFrameCounter++;
}
}
/// <summary>

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

@ -293,4 +293,21 @@ public class DrawImageTests
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
}
[Theory]
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)]
public void DrawImageAnimatedForegroundRepeatCount<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> background = provider.GetImage();
using Image<TPixel> foreground = Image.Load<TPixel>(TestFile.Create(TestImages.Gif.Giphy).Bytes);
Size size = new(foreground.Width / 4, foreground.Height / 4);
foreground.Mutate(x => x.Resize(size.Width, size.Height, KnownResamplers.Bicubic));
background.Mutate(x => x.DrawImage(foreground, Point.Empty, 1F, 0));
background.DebugSaveMultiFrame(provider);
background.CompareToReferenceOutputMultiFrame(provider, ImageComparer.TolerantPercentage(0.01f));
}
}

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/00.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:003d5986b66f90e0841c3fdfa8c595563c8e237467b8218c6fd7fa283ba28b1d
size 21154

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/01.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e1d148bd561f33c2435226c533dea539e1b21567d8f985a4059d501846e0bf30
size 21761

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/02.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:38519ad5d1d50548fada577d2bd4a8be7f76d1c3071f07bb98f1227c4bc5d303
size 20522

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/03.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e922f83fd719ba961b8720417befa119000f2c8f3956d3ac8df60c79ff56fe59
size 21291

3
tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/04.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8b7b904032ff6c142632e8c1efffe3d71c28a89d5824d9fe13dbf4ceeddcf5e8
size 21367
Loading…
Cancel
Save