Skip to content

Commit 6431723

Browse files
authored
Merge pull request #555 from SixLabors/af/FillSolidBrushTests
better FillSolidBrushTests + bugfix in FillProcessor
2 parents 01d0426 + ef9b350 commit 6431723

File tree

5 files changed

+152
-49
lines changed

5 files changed

+152
-49
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle source
6868
sourceRectangle,
6969
this.options))
7070
{
71-
amount.Span.Fill(this.options.BlendPercentage);
71+
amount.Span.Fill(1f);
7272

7373
Parallel.For(
7474
minY,
Lines changed: 129 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,164 @@
11
// Copyright (c) Six Labors and contributors.
22
// Licensed under the Apache License, Version 2.0.
33

4-
using System.Numerics;
54
using SixLabors.ImageSharp.PixelFormats;
65
using SixLabors.ImageSharp.Processing;
76
using SixLabors.ImageSharp.Processing.Drawing;
8-
using SixLabors.ImageSharp.Processing.Overlays;
7+
using SixLabors.ImageSharp.Primitives;
8+
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
9+
using SixLabors.Shapes;
910
using Xunit;
11+
// ReSharper disable InconsistentNaming
1012

1113
namespace SixLabors.ImageSharp.Tests.Drawing
1214
{
13-
public class FillSolidBrushTests : FileTestBase
15+
16+
17+
[GroupOutput("Drawing")]
18+
public class FillSolidBrushTests
1419
{
15-
[Fact]
16-
public void ImageShouldBeFloodFilledWithColorOnDefaultBackground()
20+
[Theory]
21+
[WithBlankImages(1, 1, PixelTypes.Rgba32)]
22+
[WithBlankImages(7, 4, PixelTypes.Rgba32)]
23+
[WithBlankImages(16, 7, PixelTypes.Rgba32)]
24+
[WithBlankImages(33, 32, PixelTypes.Rgba32)]
25+
[WithBlankImages(400, 500, PixelTypes.Rgba32)]
26+
public void DoesNotDependOnSize<TPixel>(TestImageProvider<TPixel> provider)
27+
where TPixel : struct, IPixel<TPixel>
1728
{
18-
string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush");
19-
using (var image = new Image<Rgba32>(500, 500))
29+
using (Image<TPixel> image = provider.GetImage())
2030
{
21-
image.Mutate(x => x.Fill(Rgba32.HotPink));
22-
image.Save($"{path}/DefaultBack.png");
23-
24-
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
25-
{
26-
Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]);
31+
TPixel color = NamedColors<TPixel>.HotPink;
32+
image.Mutate(c => c.Fill(color));
2733

28-
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
29-
}
34+
image.DebugSave(provider, appendPixelTypeToFileName: false);
35+
image.ComparePixelBufferTo(color);
3036
}
3137
}
3238

33-
[Fact]
34-
public void ImageShouldBeFloodFilledWithColor()
39+
[Theory]
40+
[WithBlankImages(16, 16, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector)]
41+
public void DoesNotDependOnSinglePixelType<TPixel>(TestImageProvider<TPixel> provider)
42+
where TPixel : struct, IPixel<TPixel>
3543
{
36-
string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush");
37-
using (var image = new Image<Rgba32>(500, 500))
44+
using (Image<TPixel> image = provider.GetImage())
3845
{
39-
image.Mutate(x => x
40-
.BackgroundColor(Rgba32.Blue)
41-
.Fill(Rgba32.HotPink));
42-
image.Save($"{path}/Simple.png");
46+
TPixel color = NamedColors<TPixel>.HotPink;
47+
image.Mutate(c => c.Fill(color));
4348

44-
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
45-
{
46-
Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]);
47-
48-
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
49-
}
49+
image.DebugSave(provider, appendSourceFileOrDescription: false);
50+
image.ComparePixelBufferTo(color);
5051
}
5152
}
5253

53-
[Fact]
54-
public void ImageShouldBeFloodFilledWithColorOpacity()
54+
[Theory]
55+
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, "Blue")]
56+
[WithSolidFilledImages(16, 16, "Yellow", PixelTypes.Rgba32, "Khaki")]
57+
public void WhenColorIsOpaque_OverridePreviousColor<TPixel>(TestImageProvider<TPixel> provider, string newColorName)
58+
where TPixel : struct, IPixel<TPixel>
5559
{
56-
string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush");
57-
using (var image = new Image<Rgba32>(500, 500))
60+
using (Image<TPixel> image = provider.GetImage())
5861
{
59-
var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150);
62+
TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>(newColorName);
63+
image.Mutate(c => c.Fill(color));
64+
65+
image.DebugSave(provider, newColorName, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false);
66+
image.ComparePixelBufferTo(color);
67+
}
68+
}
69+
70+
public static readonly TheoryData<bool, string, float, PixelBlenderMode, float> BlendData =
71+
new TheoryData<bool, string, float, PixelBlenderMode, float>()
72+
{
73+
{ false, "Blue", 0.5f, PixelBlenderMode.Normal, 1.0f },
74+
{ false, "Blue", 1.0f, PixelBlenderMode.Normal, 0.5f },
75+
{ false, "Green", 0.5f, PixelBlenderMode.Normal, 0.3f },
76+
{ false, "HotPink", 0.8f, PixelBlenderMode.Normal, 0.8f },
77+
78+
{ false, "Blue", 0.5f, PixelBlenderMode.Multiply, 1.0f },
79+
{ false, "Blue", 1.0f, PixelBlenderMode.Multiply, 0.5f },
80+
{ false, "Green", 0.5f, PixelBlenderMode.Multiply, 0.3f },
81+
{ false, "HotPink", 0.8f, PixelBlenderMode.Multiply, 0.8f },
82+
83+
{ false, "Blue", 0.5f, PixelBlenderMode.Add, 1.0f },
84+
{ false, "Blue", 1.0f, PixelBlenderMode.Add, 0.5f },
85+
{ false, "Green", 0.5f, PixelBlenderMode.Add, 0.3f },
86+
{ false, "HotPink", 0.8f, PixelBlenderMode.Add, 0.8f },
6087

61-
image.Mutate(x => x
62-
.BackgroundColor(Rgba32.Blue)
63-
.Fill(color));
64-
image.Save($"{path}/Opacity.png");
88+
{ true, "Blue", 0.5f, PixelBlenderMode.Normal, 1.0f },
89+
{ true, "Blue", 1.0f, PixelBlenderMode.Normal, 0.5f },
90+
{ true, "Green", 0.5f, PixelBlenderMode.Normal, 0.3f },
91+
{ true, "HotPink", 0.8f, PixelBlenderMode.Normal, 0.8f },
6592

66-
//shift background color towards forground color by the opacity amount
67-
var mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
93+
{ true, "Blue", 0.5f, PixelBlenderMode.Multiply, 1.0f },
94+
{ true, "Blue", 1.0f, PixelBlenderMode.Multiply, 0.5f },
95+
{ true, "Green", 0.5f, PixelBlenderMode.Multiply, 0.3f },
96+
{ true, "HotPink", 0.8f, PixelBlenderMode.Multiply, 0.8f },
6897

98+
{ true, "Blue", 0.5f, PixelBlenderMode.Add, 1.0f },
99+
{ true, "Blue", 1.0f, PixelBlenderMode.Add, 0.5f },
100+
{ true, "Green", 0.5f, PixelBlenderMode.Add, 0.3f },
101+
{ true, "HotPink", 0.8f, PixelBlenderMode.Add, 0.8f },
102+
};
69103

70-
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
104+
[Theory]
105+
[WithSolidFilledImages(nameof(BlendData), 16, 16, "Red", PixelTypes.Rgba32)]
106+
public void BlendFillColorOverBackround<TPixel>(
107+
TestImageProvider<TPixel> provider,
108+
bool triggerFillRegion,
109+
string newColorName,
110+
float alpha,
111+
PixelBlenderMode blenderMode,
112+
float blendPercentage)
113+
where TPixel : struct, IPixel<TPixel>
114+
{
115+
var vec = TestUtils.GetPixelOfNamedColor<RgbaVector>(newColorName).ToVector4();
116+
vec.W = alpha;
117+
118+
TPixel fillColor = default;
119+
fillColor.PackFromVector4(vec);
120+
121+
using (Image<TPixel> image = provider.GetImage())
122+
{
123+
TPixel bgColor = image[0, 0];
124+
125+
var options = new GraphicsOptions(false)
126+
{
127+
BlenderMode = blenderMode,
128+
BlendPercentage = blendPercentage
129+
};
130+
131+
if (triggerFillRegion)
132+
{
133+
var region = new ShapeRegion(new RectangularPolygon(0, 0, 16, 16));
134+
135+
image.Mutate(c => c.Fill(options, new SolidBrush<TPixel>(fillColor), region));
136+
}
137+
else
71138
{
72-
Assert.Equal(mergedColor, sourcePixels[9, 9]);
73-
Assert.Equal(mergedColor, sourcePixels[199, 149]);
139+
image.Mutate(c => c.Fill(options, new SolidBrush<TPixel>(fillColor)));
74140
}
141+
142+
var testOutputDetails = new
143+
{
144+
triggerFillRegion = triggerFillRegion,
145+
newColorName = newColorName,
146+
alpha = alpha,
147+
blenderMode = blenderMode,
148+
blendPercentage = blendPercentage
149+
};
150+
151+
image.DebugSave(
152+
provider,
153+
testOutputDetails,
154+
appendPixelTypeToFileName: false,
155+
appendSourceFileOrDescription: false);
156+
157+
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(blenderMode);
158+
TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage);
159+
160+
image.ComparePixelBufferTo(expectedPixel);
75161
}
76162
}
77-
78163
}
79164
}

tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public WithSolidFilledImagesAttribute(
133133
{
134134
Guard.NotNull(colorName, nameof(colorName));
135135

136-
var c = (Rgba32)typeof(Rgba32).GetTypeInfo().GetField(colorName).GetValue(null);
136+
Rgba32 c = TestUtils.GetPixelOfNamedColor<Rgba32>(colorName);
137137
this.R = c.R;
138138
this.G = c.G;
139139
this.B = c.B;

tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -346,13 +346,26 @@ public static Image<TPixel> ComparePixelBufferTo<TPixel>(
346346
Span<TPixel> expectedPixels)
347347
where TPixel : struct, IPixel<TPixel>
348348
{
349-
Span<TPixel> actual = image.GetPixelSpan();
349+
Span<TPixel> actualPixels = image.GetPixelSpan();
350350

351-
Assert.True(expectedPixels.Length == actual.Length, "Buffer sizes are not equal!");
351+
Assert.True(expectedPixels.Length == actualPixels.Length, "Buffer sizes are not equal!");
352352

353353
for (int i = 0; i < expectedPixels.Length; i++)
354354
{
355-
Assert.True(expectedPixels[i].Equals(actual[i]), $"Pixels are different on position {i}!");
355+
Assert.True(expectedPixels[i].Equals(actualPixels[i]), $"Pixels are different on position {i}!");
356+
}
357+
358+
return image;
359+
}
360+
361+
public static Image<TPixel> ComparePixelBufferTo<TPixel>(this Image<TPixel> image, TPixel expectedPixel)
362+
where TPixel : struct, IPixel<TPixel>
363+
{
364+
Span<TPixel> actualPixels = image.GetPixelSpan();
365+
366+
for (int i = 0; i < actualPixels.Length; i++)
367+
{
368+
Assert.True(expectedPixel.Equals(actualPixels[i]), $"Pixels are different on position {i}!");
356369
}
357370

358371
return image;

tests/ImageSharp.Tests/TestUtilities/TestUtils.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ internal static bool HasAll(this PixelTypes pixelTypes, PixelTypes flagsToCheck)
147147
/// <returns>The pixel types</returns>
148148
internal static PixelTypes[] GetAllPixelTypes() => (PixelTypes[])Enum.GetValues(typeof(PixelTypes));
149149

150+
internal static TPixel GetPixelOfNamedColor<TPixel>(string colorName)
151+
where TPixel : struct, IPixel<TPixel>
152+
{
153+
return (TPixel)typeof(NamedColors<TPixel>).GetTypeInfo().GetField(colorName).GetValue(null);
154+
}
150155

151156
/// <summary>
152157
/// Utility for testing image processor extension methods:

0 commit comments

Comments
 (0)