diff --git a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs index 25e504831d..79fe68c20c 100644 --- a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs @@ -22,9 +22,27 @@ public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, float opacity) + => DrawImage(source, foreground, opacity, 0); + + /// + /// 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 opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// + /// The . + 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); } /// @@ -40,9 +58,29 @@ public static IImageProcessingContext DrawImage( Image foreground, Rectangle foregroundRectangle, float opacity) + => DrawImage(source, foreground, foregroundRectangle, opacity, 0); + + /// + /// 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 opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// + /// The . + 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); } /// @@ -58,7 +96,27 @@ public static IImageProcessingContext DrawImage( Image foreground, PixelColorBlendingMode colorBlending, float opacity) - => DrawImage(source, foreground, Point.Empty, colorBlending, opacity); + => DrawImage(source, foreground, colorBlending, opacity, 0); + + /// + /// 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 color blending mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + PixelColorBlendingMode colorBlending, + float opacity, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, colorBlending, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -75,7 +133,29 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, float opacity) - => DrawImage(source, foreground, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + => DrawImage(source, foreground, foregroundRectangle, colorBlending, opacity, 0); + + /// + /// 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 color blending mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// + /// The . + 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); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -92,7 +172,29 @@ public static IImageProcessingContext DrawImage( PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) - => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity); + => DrawImage(source, foreground, colorBlending, alphaComposition, opacity, 0); + + /// + /// 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 color blending mode. + /// The alpha composition mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// + /// The . + 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); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -111,7 +213,31 @@ public static IImageProcessingContext DrawImage( PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity); + => DrawImage(source, foreground, foregroundRectangle, colorBlending, alphaComposition, opacity, 0); + + /// + /// 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 color blending mode. + /// The alpha composition mode. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// + /// The . + 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); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -124,7 +250,25 @@ public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, GraphicsOptions options) - => DrawImage(source, foreground, Point.Empty, options); + => DrawImage(source, foreground, options, 0); + + /// + /// 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 options, including the blending type and blending amount. + /// + /// 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. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + GraphicsOptions options, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, options, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -139,7 +283,27 @@ public static IImageProcessingContext DrawImage( Image foreground, Rectangle foregroundRectangle, GraphicsOptions options) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options); + => DrawImage(source, foreground, foregroundRectangle, options, 0); + + /// + /// 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 options, including the blending type and blending amount. + /// + /// 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. + /// + /// The . + public static IImageProcessingContext DrawImage( + this IImageProcessingContext source, + Image foreground, + Rectangle foregroundRectangle, + GraphicsOptions options, + int foregroundRepeatCount) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -154,9 +318,29 @@ public static IImageProcessingContext DrawImage( Image foreground, Point backgroundLocation, float opacity) + => DrawImage(source, foreground, backgroundLocation, opacity, 0); + + /// + /// 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 currently processing image at which to draw. + /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// + /// The . + 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); } /// @@ -174,9 +358,31 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, Rectangle foregroundRectangle, float opacity) + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, opacity, 0); + + /// + /// 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 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 number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + 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); } /// @@ -194,7 +400,29 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, PixelColorBlendingMode colorBlending, float opacity) - => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + => DrawImage(source, foreground, backgroundLocation, colorBlending, opacity, 0); + + /// + /// 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 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 number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + 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); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -213,7 +441,31 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, float opacity) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, opacity, 0); + + /// + /// 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 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 number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + 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); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -228,7 +480,27 @@ public static IImageProcessingContext DrawImage( Image foreground, Point backgroundLocation, GraphicsOptions options) - => DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); + => DrawImage(source, foreground, backgroundLocation, options, 0); + + /// + /// 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 currently processing image at which to draw. + /// The options containing the blend mode and opacity. + /// + /// 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. + /// + /// The . + 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); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -245,7 +517,48 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, Rectangle foregroundRectangle, GraphicsOptions options) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options, 0); + + /// + /// 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 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 number of times the foreground frames are allowed to loop while applying this operation across successive frames. + /// A value of 0 means loop indefinitely. + /// + /// The . + 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); + + /// + /// 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 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 foreground, + Point backgroundLocation, + PixelColorBlendingMode colorBlending, + PixelAlphaCompositionMode alphaComposition, + float opacity) + => DrawImage(source, foreground, backgroundLocation, colorBlending, alphaComposition, opacity, 0); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -256,6 +569,10 @@ public static IImageProcessingContext DrawImage( /// The color blending to apply. /// The alpha composition mode. /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// /// The . public static IImageProcessingContext DrawImage( this IImageProcessingContext source, @@ -263,8 +580,30 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, + float opacity, + int foregroundRepeatCount) + => source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity, foregroundRepeatCount)); + + /// + /// 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 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 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); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -276,6 +615,10 @@ public static IImageProcessingContext DrawImage( /// The color blending to apply. /// The alpha composition mode. /// The opacity of the image to draw. Must be between 0 and 1. + /// + /// 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. + /// /// The . public static IImageProcessingContext DrawImage( this IImageProcessingContext source, @@ -284,8 +627,9 @@ public static IImageProcessingContext DrawImage( 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); } diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs index 6ecf16fc6b..675d1a3119 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -19,13 +19,15 @@ public class DrawImageProcessor : IImageProcessor /// 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. + /// The number of times the foreground frames are allowed to loop. 0 means infinitely. 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 DrawImageProcessor( /// 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. + /// The number of times the foreground frames are allowed to loop. 0 means infinitely. 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 DrawImageProcessor( this.ColorBlendingMode = colorBlendingMode; this.AlphaCompositionMode = alphaCompositionMode; this.Opacity = opacity; + this.ForegroundRepeatCount = foregroundRepeatCount; } /// @@ -84,6 +89,11 @@ public DrawImageProcessor( /// public float Opacity { get; } + /// + /// Gets the number of times the foreground frames are allowed to loop. 0 means infinitely. + /// + public int ForegroundRepeatCount { get; } + /// public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixelBg : unmanaged, IPixel @@ -122,6 +132,7 @@ public void Visit(Image image) this.definition.ForegroundRectangle, this.definition.ColorBlendingMode, this.definition.AlphaCompositionMode, - this.definition.Opacity); + this.definition.Opacity, + this.definition.ForegroundRepeatCount); } } diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index d2a99ce921..7a1ffb85f9 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -17,6 +17,12 @@ internal class DrawImageProcessor : ImageProcessor where TPixelBg : unmanaged, IPixel where TPixelFg : unmanaged, IPixel { + /// + /// Counts how many times has been called for this processor instance. + /// Used to select the current foreground frame. + /// + private int foregroundFrameCounter; + /// /// Initializes a new instance of the class. /// @@ -28,6 +34,10 @@ internal class DrawImageProcessor : ImageProcessor /// 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. Must be between 0 and 1. + /// + /// 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. + /// public DrawImageProcessor( Configuration configuration, Image foregroundImage, @@ -36,9 +46,11 @@ public DrawImageProcessor( 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 @@ public DrawImageProcessor( this.Opacity = opacity; this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.BackgroundLocation = backgroundLocation; + this.ForegroundRepeatCount = foregroundRepeatCount; } /// @@ -73,6 +86,12 @@ public DrawImageProcessor( /// public Point BackgroundLocation { get; } + /// + /// 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. + /// + public int ForegroundRepeatCount { get; } + /// protected override void OnFrameApply(ImageFrame source) { @@ -114,12 +133,13 @@ protected override void OnFrameApply(ImageFrame source) // 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.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 @@ protected override void OnFrameApply(ImageFrame source) 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++; + } } /// diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 1d0fdf62d3..7f87111e80 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -293,4 +293,21 @@ public void Issue2603(TestImageProvider provider) appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void DrawImageAnimatedForegroundRepeatCount(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image background = provider.GetImage(); + using Image foreground = Image.Load(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)); + } } diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/00.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/00.png new file mode 100644 index 0000000000..9e3e48a528 --- /dev/null +++ b/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 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/01.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/01.png new file mode 100644 index 0000000000..7d8babf99f --- /dev/null +++ b/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 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/02.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/02.png new file mode 100644 index 0000000000..108ccd1f52 --- /dev/null +++ b/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 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/03.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/03.png new file mode 100644 index 0000000000..28c79f8c2f --- /dev/null +++ b/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 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/04.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawImageAnimatedForegroundRepeatCount_Rgba32_giphy.gif/04.png new file mode 100644 index 0000000000..401939430d --- /dev/null +++ b/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