Skip to content

Commit efd7f4c

Browse files
Merge branch 'main' into js/tiff-icc
2 parents 9fbe5a4 + 9a0b946 commit efd7f4c

File tree

9 files changed

+438
-24
lines changed

9 files changed

+438
-24
lines changed

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

Lines changed: 361 additions & 17 deletions
Large diffs are not rendered by default.

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ public class DrawImageProcessor : IImageProcessor
1919
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
2020
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
2121
/// <param name="opacity">The opacity of the image to blend.</param>
22+
/// <param name="foregroundRepeatCount">The number of times the foreground frames are allowed to loop. 0 means infinitely.</param>
2223
public DrawImageProcessor(
2324
Image foreground,
2425
Point backgroundLocation,
2526
PixelColorBlendingMode colorBlendingMode,
2627
PixelAlphaCompositionMode alphaCompositionMode,
27-
float opacity)
28-
: this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity)
28+
float opacity,
29+
int foregroundRepeatCount)
30+
: this(foreground, backgroundLocation, foreground.Bounds, colorBlendingMode, alphaCompositionMode, opacity, foregroundRepeatCount)
2931
{
3032
}
3133

@@ -38,20 +40,23 @@ public DrawImageProcessor(
3840
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
3941
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
4042
/// <param name="opacity">The opacity of the image to blend.</param>
43+
/// <param name="foregroundRepeatCount">The number of times the foreground frames are allowed to loop. 0 means infinitely.</param>
4144
public DrawImageProcessor(
4245
Image foreground,
4346
Point backgroundLocation,
4447
Rectangle foregroundRectangle,
4548
PixelColorBlendingMode colorBlendingMode,
4649
PixelAlphaCompositionMode alphaCompositionMode,
47-
float opacity)
50+
float opacity,
51+
int foregroundRepeatCount)
4852
{
4953
this.ForeGround = foreground;
5054
this.BackgroundLocation = backgroundLocation;
5155
this.ForegroundRectangle = foregroundRectangle;
5256
this.ColorBlendingMode = colorBlendingMode;
5357
this.AlphaCompositionMode = alphaCompositionMode;
5458
this.Opacity = opacity;
59+
this.ForegroundRepeatCount = foregroundRepeatCount;
5560
}
5661

5762
/// <summary>
@@ -84,6 +89,11 @@ public DrawImageProcessor(
8489
/// </summary>
8590
public float Opacity { get; }
8691

92+
/// <summary>
93+
/// Gets the number of times the foreground frames are allowed to loop. 0 means infinitely.
94+
/// </summary>
95+
public int ForegroundRepeatCount { get; }
96+
8797
/// <inheritdoc />
8898
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>(Configuration configuration, Image<TPixelBg> source, Rectangle sourceRectangle)
8999
where TPixelBg : unmanaged, IPixel<TPixelBg>
@@ -122,6 +132,7 @@ public void Visit<TPixelFg>(Image<TPixelFg> image)
122132
this.definition.ForegroundRectangle,
123133
this.definition.ColorBlendingMode,
124134
this.definition.AlphaCompositionMode,
125-
this.definition.Opacity);
135+
this.definition.Opacity,
136+
this.definition.ForegroundRepeatCount);
126137
}
127138
}

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

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
1717
where TPixelBg : unmanaged, IPixel<TPixelBg>
1818
where TPixelFg : unmanaged, IPixel<TPixelFg>
1919
{
20+
/// <summary>
21+
/// Counts how many times <see cref="OnFrameApply"/> has been called for this processor instance.
22+
/// Used to select the current foreground frame.
23+
/// </summary>
24+
private int foregroundFrameCounter;
25+
2026
/// <summary>
2127
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelBg, TPixelFg}"/> class.
2228
/// </summary>
@@ -28,6 +34,10 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
2834
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
2935
/// <param name="alphaCompositionMode">The alpha blending mode to use when drawing the image.</param>
3036
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
37+
/// <param name="foregroundRepeatCount">
38+
/// The number of times the foreground frames are allowed to loop while applying this processor across successive frames.
39+
/// A value of 0 means loop indefinitely.
40+
/// </param>
3141
public DrawImageProcessor(
3242
Configuration configuration,
3343
Image<TPixelFg> foregroundImage,
@@ -36,16 +46,19 @@ public DrawImageProcessor(
3646
Rectangle foregroundRectangle,
3747
PixelColorBlendingMode colorBlendingMode,
3848
PixelAlphaCompositionMode alphaCompositionMode,
39-
float opacity)
49+
float opacity,
50+
int foregroundRepeatCount)
4051
: base(configuration, backgroundImage, backgroundImage.Bounds)
4152
{
53+
Guard.MustBeGreaterThanOrEqualTo(foregroundRepeatCount, 0, nameof(foregroundRepeatCount));
4254
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
4355

4456
this.ForegroundImage = foregroundImage;
4557
this.ForegroundRectangle = foregroundRectangle;
4658
this.Opacity = opacity;
4759
this.Blender = PixelOperations<TPixelBg>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
4860
this.BackgroundLocation = backgroundLocation;
61+
this.ForegroundRepeatCount = foregroundRepeatCount;
4962
}
5063

5164
/// <summary>
@@ -73,6 +86,12 @@ public DrawImageProcessor(
7386
/// </summary>
7487
public Point BackgroundLocation { get; }
7588

89+
/// <summary>
90+
/// Gets the number of times the foreground frames are allowed to loop while applying this processor across
91+
/// successive frames. A value of 0 means loop indefinitely.
92+
/// </summary>
93+
public int ForegroundRepeatCount { get; }
94+
7695
/// <inheritdoc/>
7796
protected override void OnFrameApply(ImageFrame<TPixelBg> source)
7897
{
@@ -114,12 +133,13 @@ protected override void OnFrameApply(ImageFrame<TPixelBg> source)
114133
// Sanitize the dimensions so that we don't try and sample outside the image.
115134
Rectangle backgroundRectangle = Rectangle.Intersect(new Rectangle(left, top, width, height), this.SourceRectangle);
116135
Configuration configuration = this.Configuration;
136+
int currentFrameIndex = this.foregroundFrameCounter % this.ForegroundImage.Frames.Count;
117137

118-
DrawImageProcessor<TPixelBg, TPixelFg>.RowOperation operation =
138+
RowOperation operation =
119139
new(
120140
configuration,
121141
source.PixelBuffer,
122-
this.ForegroundImage.Frames.RootFrame.PixelBuffer,
142+
this.ForegroundImage.Frames[currentFrameIndex].PixelBuffer,
123143
backgroundRectangle,
124144
foregroundRectangle,
125145
this.Blender,
@@ -129,6 +149,13 @@ protected override void OnFrameApply(ImageFrame<TPixelBg> source)
129149
configuration,
130150
new Rectangle(0, 0, foregroundRectangle.Width, foregroundRectangle.Height),
131151
in operation);
152+
153+
// The repeat count only affects how the foreground frame advances across successive background frames.
154+
// When exhausted, the selected foreground frame stops advancing.
155+
if (this.ForegroundRepeatCount is 0 || this.foregroundFrameCounter / this.ForegroundImage.Frames.Count < this.ForegroundRepeatCount)
156+
{
157+
this.foregroundFrameCounter++;
158+
}
132159
}
133160

134161
/// <summary>

tests/ImageSharp.Tests/Drawing/DrawImageTests.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,21 @@ public void Issue2603<TPixel>(TestImageProvider<TPixel> provider)
293293
appendPixelTypeToFileName: false,
294294
appendSourceFileOrDescription: false);
295295
}
296+
297+
[Theory]
298+
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)]
299+
public void DrawImageAnimatedForegroundRepeatCount<TPixel>(TestImageProvider<TPixel> provider)
300+
where TPixel : unmanaged, IPixel<TPixel>
301+
{
302+
using Image<TPixel> background = provider.GetImage();
303+
using Image<TPixel> foreground = Image.Load<TPixel>(TestFile.Create(TestImages.Gif.Giphy).Bytes);
304+
305+
Size size = new(foreground.Width / 4, foreground.Height / 4);
306+
foreground.Mutate(x => x.Resize(size.Width, size.Height, KnownResamplers.Bicubic));
307+
308+
background.Mutate(x => x.DrawImage(foreground, Point.Empty, 1F, 0));
309+
310+
background.DebugSaveMultiFrame(provider);
311+
background.CompareToReferenceOutputMultiFrame(provider, ImageComparer.TolerantPercentage(0.01f));
312+
}
296313
}
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)