From 60149fc64fb994d9089c7fad0c19e205efc38d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=88=E7=BA=BE?= Date: Sat, 11 Oct 2025 10:57:38 +0800 Subject: [PATCH 1/3] Enhance DrawImage functionality by adding repeatCount parameter for animation control in DrawImageExtensions and DrawImageProcessor classes. --- .../Extensions/Drawing/DrawImageExtensions.cs | 80 +++++++++++-------- .../Processors/Drawing/DrawImageProcessor.cs | 19 ++++- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 20 ++++- 3 files changed, 81 insertions(+), 38 deletions(-) diff --git a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs index 25e504831d..a6a02e133a 100644 --- a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs @@ -21,10 +21,11 @@ public static class DrawImageExtensions public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, - float opacity) + float opacity, + int repeatCount) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); + return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, repeatCount); } /// @@ -39,10 +40,11 @@ public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, Rectangle foregroundRectangle, - float opacity) + float opacity, + int repeatCount) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); + return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, repeatCount); } /// @@ -57,8 +59,9 @@ public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, PixelColorBlendingMode colorBlending, - float opacity) - => DrawImage(source, foreground, Point.Empty, colorBlending, opacity); + float opacity, + int repeatCount) + => DrawImage(source, foreground, Point.Empty, colorBlending, opacity, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -74,8 +77,9 @@ public static IImageProcessingContext DrawImage( Image foreground, Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, - float opacity) - => DrawImage(source, foreground, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + float opacity, + int repeatCount) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, opacity, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -91,8 +95,9 @@ public static IImageProcessingContext DrawImage( Image foreground, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, - float opacity) - => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity); + float opacity, + int repeatCount) + => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -110,8 +115,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 repeatCount) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -123,8 +129,9 @@ public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, - GraphicsOptions options) - => DrawImage(source, foreground, Point.Empty, options); + GraphicsOptions options, + int repeatCount) + => DrawImage(source, foreground, Point.Empty, options, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -138,8 +145,9 @@ public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, Rectangle foregroundRectangle, - GraphicsOptions options) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options); + GraphicsOptions options, + int repeatCount) + => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -153,10 +161,11 @@ public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image foreground, Point backgroundLocation, - float opacity) + float opacity, + int repeatCount) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity); + return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, repeatCount); } /// @@ -173,10 +182,11 @@ public static IImageProcessingContext DrawImage( Image foreground, Point backgroundLocation, Rectangle foregroundRectangle, - float opacity) + float opacity, + int repeatCount) { 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, repeatCount); } /// @@ -193,8 +203,9 @@ public static IImageProcessingContext DrawImage( Image foreground, Point backgroundLocation, PixelColorBlendingMode colorBlending, - float opacity) - => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity); + float opacity, + int repeatCount) + => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -212,8 +223,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 repeatCount) + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -227,8 +239,9 @@ 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 repeatCount) + => DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -244,8 +257,9 @@ public static IImageProcessingContext DrawImage( Image foreground, Point backgroundLocation, Rectangle foregroundRectangle, - GraphicsOptions options) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage); + GraphicsOptions options, + int repeatCount) + => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage, repeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -263,8 +277,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 repeatCount) + => source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity, repeatCount)); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -284,8 +299,9 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, - float opacity) => + float opacity, + int repeatCount) => source.ApplyProcessor( - new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity), + new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity, repeatCount), foregroundRectangle); } diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs index 6ecf16fc6b..3e17dae517 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 loop count. The number of times to loop the animation. 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 repeatCount) + : this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity, repeatCount) { } @@ -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 loop count. The number of times to loop the animation. 0 means infinitely. public DrawImageProcessor( Image foreground, Point backgroundLocation, Rectangle foregroundRectangle, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, - float opacity) + float opacity, + int repeatCount) { this.ForeGround = foreground; this.BackgroundLocation = backgroundLocation; @@ -52,6 +56,7 @@ public DrawImageProcessor( this.ColorBlendingMode = colorBlendingMode; this.AlphaCompositionMode = alphaCompositionMode; this.Opacity = opacity; + this.RepeatCount = repeatCount; } /// @@ -84,6 +89,11 @@ public DrawImageProcessor( /// public float Opacity { get; } + /// + /// Gets the loop count. The number of times to loop the animation. 0 means infinitely. + /// + public int RepeatCount { 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.RepeatCount); } } diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index d2a99ce921..c72a10d8ae 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -17,6 +17,8 @@ internal class DrawImageProcessor : ImageProcessor where TPixelBg : unmanaged, IPixel where TPixelFg : unmanaged, IPixel { + private int currentFrameLoop; + /// /// Initializes a new instance of the class. /// @@ -28,6 +30,7 @@ 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 loop count. The number of times to loop the animation. 0 means infinitely. public DrawImageProcessor( Configuration configuration, Image foregroundImage, @@ -36,9 +39,11 @@ public DrawImageProcessor( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, - float opacity) + float opacity, + int repeatCount) : base(configuration, backgroundImage, backgroundImage.Bounds) { + Guard.MustBeGreaterThanOrEqualTo(repeatCount, 0, nameof(repeatCount)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.ForegroundImage = foregroundImage; @@ -73,6 +78,11 @@ public DrawImageProcessor( /// public Point BackgroundLocation { get; } + /// + /// Gets the loop count. The number of times to loop the animation. 0 means infinitely. + /// + public int RepeatCount { get; } + /// protected override void OnFrameApply(ImageFrame source) { @@ -114,12 +124,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.currentFrameLoop % this.ForegroundImage.Frames.Count; DrawImageProcessor.RowOperation operation = new( configuration, source.PixelBuffer, - this.ForegroundImage.Frames.RootFrame.PixelBuffer, + this.ForegroundImage.Frames[currentFrameIndex].PixelBuffer, backgroundRectangle, foregroundRectangle, this.Blender, @@ -129,6 +140,11 @@ protected override void OnFrameApply(ImageFrame source) configuration, new Rectangle(0, 0, foregroundRectangle.Width, foregroundRectangle.Height), in operation); + + if (this.RepeatCount is 0 || this.currentFrameLoop / this.ForegroundImage.Frames.Count <= this.RepeatCount) + { + this.currentFrameLoop++; + } } /// From e749f3fa835de6d2543c60023e49386c2a45bcc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=88=E7=BA=BE?= Date: Sat, 11 Oct 2025 11:17:26 +0800 Subject: [PATCH 2/3] Fix condition for frame loop increment in DrawImageProcessor to ensure correct repeat count handling. --- .../Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index c72a10d8ae..0018888653 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -141,7 +141,7 @@ protected override void OnFrameApply(ImageFrame source) new Rectangle(0, 0, foregroundRectangle.Width, foregroundRectangle.Height), in operation); - if (this.RepeatCount is 0 || this.currentFrameLoop / this.ForegroundImage.Frames.Count <= this.RepeatCount) + if (this.RepeatCount is 0 || this.currentFrameLoop / this.ForegroundImage.Frames.Count < this.RepeatCount) { this.currentFrameLoop++; } From 74e8321cd551f4a1b23a7a51a4cc891b4bddbf1d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 20 Jan 2026 16:55:35 +1000 Subject: [PATCH 3/3] Update field names and fix assignment bug --- .../Extensions/Drawing/DrawImageExtensions.cs | 128 +++++++++++++----- .../Processors/Drawing/DrawImageProcessor.cs | 18 +-- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 31 +++-- 3 files changed, 126 insertions(+), 51 deletions(-) diff --git a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs index a6a02e133a..42051ca939 100644 --- a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs @@ -17,15 +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, - int repeatCount) + int foregroundRepeatCount = 0) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, repeatCount); + return DrawImage(source, foreground, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -35,16 +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, - int repeatCount) + int foregroundRepeatCount = 0) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, repeatCount); + return DrawImage(source, foreground, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -54,14 +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, - int repeatCount) - => DrawImage(source, foreground, Point.Empty, colorBlending, opacity, repeatCount); + 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. @@ -71,6 +83,10 @@ 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, @@ -78,8 +94,8 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, float opacity, - int repeatCount) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, opacity, repeatCount); + 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. @@ -89,6 +105,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, @@ -96,8 +116,8 @@ public static IImageProcessingContext DrawImage( PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity, - int repeatCount) - => DrawImage(source, foreground, Point.Empty, colorBlending, alphaComposition, opacity, repeatCount); + 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. @@ -108,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, @@ -116,8 +140,8 @@ public static IImageProcessingContext DrawImage( PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity, - int repeatCount) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, colorBlending, alphaComposition, opacity, repeatCount); + 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. @@ -125,13 +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, - int repeatCount) - => DrawImage(source, foreground, Point.Empty, options, repeatCount); + int foregroundRepeatCount = 0) + => DrawImage(source, foreground, Point.Empty, options, foregroundRepeatCount); /// /// Draws the given image together with the currently processing image by blending their pixels. @@ -140,14 +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, - int repeatCount) - => DrawImage(source, foreground, Point.Empty, foregroundRectangle, options, repeatCount); + 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. @@ -156,16 +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, - int repeatCount) + int foregroundRepeatCount = 0) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, repeatCount); + return DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -176,6 +212,10 @@ 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, @@ -183,10 +223,10 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, Rectangle foregroundRectangle, float opacity, - int repeatCount) + int foregroundRepeatCount = 0) { GraphicsOptions options = source.GetGraphicsOptions(); - return DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, repeatCount); + return DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, opacity, foregroundRepeatCount); } /// @@ -197,6 +237,10 @@ 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, @@ -204,8 +248,8 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, PixelColorBlendingMode colorBlending, float opacity, - int repeatCount) - => DrawImage(source, foreground, backgroundLocation, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, repeatCount); + 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. @@ -216,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, @@ -224,8 +272,8 @@ public static IImageProcessingContext DrawImage( Rectangle foregroundRectangle, PixelColorBlendingMode colorBlending, float opacity, - int repeatCount) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity, repeatCount); + 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. @@ -234,14 +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, - int repeatCount) - => DrawImage(source, foreground, backgroundLocation, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage, repeatCount); + 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. @@ -251,6 +303,10 @@ 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, @@ -258,8 +314,8 @@ public static IImageProcessingContext DrawImage( Point backgroundLocation, Rectangle foregroundRectangle, GraphicsOptions options, - int repeatCount) - => DrawImage(source, foreground, backgroundLocation, foregroundRectangle, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage, repeatCount); + 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. @@ -270,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, @@ -278,8 +338,8 @@ public static IImageProcessingContext DrawImage( PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity, - int repeatCount) - => source.ApplyProcessor(new DrawImageProcessor(foreground, backgroundLocation, foreground.Bounds, colorBlending, alphaComposition, opacity, repeatCount)); + 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. @@ -291,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, @@ -300,8 +364,8 @@ public static IImageProcessingContext DrawImage( PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity, - int repeatCount) => + int foregroundRepeatCount = 0) => source.ApplyProcessor( - new DrawImageProcessor(foreground, backgroundLocation, foregroundRectangle, colorBlending, alphaComposition, opacity, repeatCount), + 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 3e17dae517..675d1a3119 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -19,15 +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 loop count. The number of times to loop the animation. 0 means infinitely. + /// 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, - int repeatCount) - : this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity, repeatCount) + int foregroundRepeatCount) + : this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity, foregroundRepeatCount) { } @@ -40,7 +40,7 @@ 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 loop count. The number of times to loop the animation. 0 means infinitely. + /// The number of times the foreground frames are allowed to loop. 0 means infinitely. public DrawImageProcessor( Image foreground, Point backgroundLocation, @@ -48,7 +48,7 @@ public DrawImageProcessor( PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity, - int repeatCount) + int foregroundRepeatCount) { this.ForeGround = foreground; this.BackgroundLocation = backgroundLocation; @@ -56,7 +56,7 @@ public DrawImageProcessor( this.ColorBlendingMode = colorBlendingMode; this.AlphaCompositionMode = alphaCompositionMode; this.Opacity = opacity; - this.RepeatCount = repeatCount; + this.ForegroundRepeatCount = foregroundRepeatCount; } /// @@ -90,9 +90,9 @@ public DrawImageProcessor( public float Opacity { get; } /// - /// Gets the loop count. The number of times to loop the animation. 0 means infinitely. + /// Gets the number of times the foreground frames are allowed to loop. 0 means infinitely. /// - public int RepeatCount { get; } + public int ForegroundRepeatCount { get; } /// public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) @@ -133,6 +133,6 @@ public void Visit(Image image) this.definition.ColorBlendingMode, this.definition.AlphaCompositionMode, this.definition.Opacity, - this.definition.RepeatCount); + 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 0018888653..7a1ffb85f9 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -17,7 +17,11 @@ internal class DrawImageProcessor : ImageProcessor where TPixelBg : unmanaged, IPixel where TPixelFg : unmanaged, IPixel { - private int currentFrameLoop; + /// + /// 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. @@ -30,7 +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 loop count. The number of times to loop the animation. 0 means infinitely. + /// + /// 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, @@ -40,10 +47,10 @@ public DrawImageProcessor( PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity, - int repeatCount) + int foregroundRepeatCount) : base(configuration, backgroundImage, backgroundImage.Bounds) { - Guard.MustBeGreaterThanOrEqualTo(repeatCount, 0, nameof(repeatCount)); + Guard.MustBeGreaterThanOrEqualTo(foregroundRepeatCount, 0, nameof(foregroundRepeatCount)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.ForegroundImage = foregroundImage; @@ -51,6 +58,7 @@ public DrawImageProcessor( this.Opacity = opacity; this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.BackgroundLocation = backgroundLocation; + this.ForegroundRepeatCount = foregroundRepeatCount; } /// @@ -79,9 +87,10 @@ public DrawImageProcessor( public Point BackgroundLocation { get; } /// - /// Gets the loop count. The number of times to loop the animation. 0 means infinitely. + /// 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 RepeatCount { get; } + public int ForegroundRepeatCount { get; } /// protected override void OnFrameApply(ImageFrame source) @@ -124,9 +133,9 @@ 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.currentFrameLoop % this.ForegroundImage.Frames.Count; + int currentFrameIndex = this.foregroundFrameCounter % this.ForegroundImage.Frames.Count; - DrawImageProcessor.RowOperation operation = + RowOperation operation = new( configuration, source.PixelBuffer, @@ -141,9 +150,11 @@ protected override void OnFrameApply(ImageFrame source) new Rectangle(0, 0, foregroundRectangle.Width, foregroundRectangle.Height), in operation); - if (this.RepeatCount is 0 || this.currentFrameLoop / this.ForegroundImage.Frames.Count < this.RepeatCount) + // 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.currentFrameLoop++; + this.foregroundFrameCounter++; } }