diff --git a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs index 25e504831d..42051ca939 100644 --- a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs @@ -17,14 +17,19 @@ public static class DrawImageExtensions /// 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) + float opacity, + int foregroundRepeatCount = 0) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); + return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -34,15 +39,20 @@ public static IImageProcessingContext DrawImage( /// 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) + float opacity, + int foregroundRepeatCount = 0) { 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); } /// @@ -52,13 +62,18 @@ public static IImageProcessingContext DrawImage( /// 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) - => DrawImage(source, foreground, Point.Empty, colorBlending, opacity); + float opacity, + int foregroundRepeatCount = 0) + => DrawImage(source, foreground, Point.Empty, colorBlending, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -68,14 +83,19 @@ public static IImageProcessingContext DrawImage( /// 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) - => DrawImage(source, foreground, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + float opacity, + int foregroundRepeatCount = 0) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -85,14 +105,19 @@ public static IImageProcessingContext DrawImage( /// 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) - => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity); + float opacity, + int foregroundRepeatCount = 0) + => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -103,6 +128,10 @@ public static IImageProcessingContext DrawImage( /// 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, @@ -110,8 +139,9 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, - float opacity) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity); + float opacity, + int foregroundRepeatCount = 0) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -119,12 +149,17 @@ public static IImageProcessingContext DrawImage( /// 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) - => DrawImage(source, foreground, Point.Empty, options); + GraphicsOptions options, + int foregroundRepeatCount = 0) + => DrawImage(source, foreground, Point.Empty, options, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -133,13 +168,18 @@ public static IImageProcessingContext DrawImage( /// 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) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options); + GraphicsOptions options, + int foregroundRepeatCount = 0) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -148,15 +188,20 @@ public static IImageProcessingContext DrawImage( /// 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) + float opacity, + int foregroundRepeatCount = 0) { 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); } /// @@ -167,16 +212,21 @@ public static IImageProcessingContext DrawImage( /// 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) + float opacity, + int foregroundRepeatCount = 0) { 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); } /// @@ -187,14 +237,19 @@ public static IImageProcessingContext DrawImage( /// 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) - => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + float opacity, + int foregroundRepeatCount = 0) + => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -205,6 +260,10 @@ public static IImageProcessingContext DrawImage( /// 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, @@ -212,8 +271,9 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, - float opacity) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + float opacity, + int foregroundRepeatCount = 0) + => 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. @@ -222,13 +282,18 @@ public static IImageProcessingContext DrawImage( /// 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) - => DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); + GraphicsOptions options, + int foregroundRepeatCount = 0) + => 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. @@ -238,14 +303,19 @@ public static IImageProcessingContext DrawImage( /// 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) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); + GraphicsOptions options, + int foregroundRepeatCount = 0) + => 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. @@ -256,6 +326,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 +337,9 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, - float opacity) - => source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity)); + float opacity, + int foregroundRepeatCount = 0) + => 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. @@ -276,6 +351,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 +363,9 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, - float opacity) => + float opacity, + int foregroundRepeatCount = 0) => 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++; + } } ///