From ba92b1c755c27d065572c78de6eac6b679a2a60d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 12 May 2019 02:34:48 +0200 Subject: [PATCH] FillPolygonTests --- src/ImageSharp/Color/Color.Conversions.cs | 14 + src/ImageSharp/Color/Color.cs | 12 + tests/ImageSharp.Tests/Color/ColorTests.cs | 11 + .../Drawing/FillPolygonTests.cs | 155 +++++++++++ .../Drawing/RecolorImageTests.cs | 3 +- .../Drawing/SolidPolygonTests.cs | 240 ------------------ tests/Images/External | 2 +- 7 files changed, 195 insertions(+), 242 deletions(-) create mode 100644 tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs delete mode 100644 tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs index 001aee5a4f..20dca67706 100644 --- a/src/ImageSharp/Color/Color.Conversions.cs +++ b/src/ImageSharp/Color/Color.Conversions.cs @@ -117,6 +117,13 @@ namespace SixLabors.ImageSharp /// The . public static implicit operator Color(Bgr24 source) => new Color(source); + /// + /// Converts an to . + /// + /// The . + /// The . + public static explicit operator Color(Vector4 source) => new Color(source); + /// /// Converts a to . /// @@ -158,5 +165,12 @@ namespace SixLabors.ImageSharp /// The . /// The . public static implicit operator Bgr24(Color color) => color.data.ToBgr24(); + + /// + /// Converts a to . + /// + /// The . + /// The . + public static explicit operator Vector4(Color color) => color.data.ToVector4(); } } \ No newline at end of file diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index f2f8578d83..fe35033147 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -94,6 +94,18 @@ namespace SixLabors.ImageSharp return new Color(rgba); } + /// + /// Alters the alpha channel of the color, returning a new instance. + /// + /// The new value of alpha [0..1]. + /// The color having it's alpha channel altered. + public Color WithAlpha(float alpha) + { + Vector4 v = (Vector4)this; + v.W = alpha; + return new Color(v); + } + /// /// Gets the hexadecimal representation of the color instance in rrggbbaa form. /// diff --git a/tests/ImageSharp.Tests/Color/ColorTests.cs b/tests/ImageSharp.Tests/Color/ColorTests.cs index 729ef94e3c..e9e22ccdd9 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.cs @@ -13,6 +13,17 @@ namespace SixLabors.ImageSharp.Tests { public partial class ColorTests { + [Fact] + public void WithAlpha() + { + Color c1 = Color.FromRgba(111, 222, 55, 255); + Color c2 = c1.WithAlpha(0.5f); + + Rgba32 expected = new Rgba32(111, 222, 55, 128); + + Assert.Equal(expected, (Rgba32)c2); + } + [Fact] public void Equality_WhenTrue() { diff --git a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs new file mode 100644 index 0000000000..43029e6957 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs @@ -0,0 +1,155 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Shapes; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing")] + public class FillPolygonTests + { + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, true)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, true)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, false)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, true)] + public void FillPolygon_Solid(TestImageProvider provider, string colorName, float alpha, bool antialias) + where TPixel : struct, IPixel + { + SixLabors.Primitives.PointF[] simplePath = + { + new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) + }; + Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); + + GraphicsOptions options = new GraphicsOptions(antialias); + + string aa = antialias ? "" : "_NoAntialias"; + FormattableString outputDetails = $"{colorName}_A{alpha}{aa}"; + + provider.RunValidatingProcessorTest( + c => c.FillPolygon(options, color.ToPixel(), simplePath), + outputDetails, + appendSourceFileOrDescription: false); + } + + [Theory] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32)] + public void FillPolygon_Concave(TestImageProvider provider) + where TPixel : struct, IPixel + { + var points = new SixLabors.Primitives.PointF[] + { + new Vector2(8, 8), + new Vector2(64, 8), + new Vector2(64, 64), + new Vector2(120, 64), + new Vector2(120, 120), + new Vector2(8, 120) + }; + + Color color = Color.LightGreen; + + provider.RunValidatingProcessorTest( + c => c.FillPolygon(color.ToPixel(), points), + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32)] + public void FillPolygon_Pattern(TestImageProvider provider) + where TPixel : struct, IPixel + { + SixLabors.Primitives.PointF[] simplePath = + { + new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) + }; + Color color = Color.Yellow; + + var brush = Brushes.Horizontal(color.ToPixel()); + + provider.RunValidatingProcessorTest( + c => c.FillPolygon(brush, simplePath), + appendSourceFileOrDescription: false); + } + + [Theory] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, TestImages.Png.Ducky)] + [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, TestImages.Bmp.Car)] + public void FillPolygon_ImageBrush(TestImageProvider provider, string brushImageName) + where TPixel : struct, IPixel + { + SixLabors.Primitives.PointF[] simplePath = + { + new Vector2(10, 10), new Vector2(200, 50), new Vector2(50, 200) + }; + + using (Image brushImage = Image.Load(TestFile.Create(brushImageName).Bytes)) + { + var brush = new ImageBrush(brushImage); + + provider.RunValidatingProcessorTest( + c => c.FillPolygon(brush, simplePath), + System.IO.Path.GetFileNameWithoutExtension(brushImageName), + appendSourceFileOrDescription: false); + } + } + + [Theory] + [WithBasicTestPatternImages(250, 250, PixelTypes.Rgba32)] + public void Fill_RectangularPolygon(TestImageProvider provider) + where TPixel : struct, IPixel + { + var polygon = new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140); + Color color = Color.White; + + provider.RunValidatingProcessorTest( + c => c.Fill(color.ToPixel(), polygon), + appendSourceFileOrDescription: false); + } + + [Theory] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 50, 0f)] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 60, 20f)] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 60, -180f)] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 5, 70, 0f)] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 7, 80, -180f)] + public void Fill_RegularPolygon(TestImageProvider provider, int vertices, float radius, float angleDeg) + where TPixel : struct, IPixel + { + float angle = GeometryUtilities.DegreeToRadian(angleDeg); + var polygon = new RegularPolygon(100, 100, vertices, radius, angle); + Color color = Color.Yellow; + + FormattableString testOutput = $"V({vertices})_R({radius})_Ang({angleDeg})"; + provider.RunValidatingProcessorTest( + c => c.Fill(color.ToPixel(), polygon), + testOutput, + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); + } + + [Theory] + [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32)] + public void Fill_EllipsePolygon(TestImageProvider provider) + where TPixel : struct, IPixel + { + var polygon = new EllipsePolygon(100, 100, 80, 120); + Color color = Color.Azure; + + provider.RunValidatingProcessorTest( + c => c.Fill(color.ToPixel(), polygon), + appendSourceFileOrDescription: false, + appendPixelTypeToFileName: false); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs index cb671a53d4..461ca700f7 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs @@ -6,9 +6,10 @@ using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; + using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Drawing { [GroupOutput("Drawing")] public class RecolorImageTests diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs deleted file mode 100644 index 6a299aad7b..0000000000 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Shapes; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - public class SolidPolygonTests : FileTestBase - { - [Fact] - public void ImageShouldBeOverlayedByFilledPolygon() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - SixLabors.Primitives.PointF[] simplePath = { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.FillPolygon(new GraphicsOptions(true), Rgba32.HotPink, simplePath)); - image.Save($"{path}/Simple.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonWithPattern() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - using (var image = new Image(500, 500)) - { - image.Mutate( - x => x.FillPolygon(new GraphicsOptions(true), Brushes.Horizontal(Rgba32.HotPink), simplePath)); - image.Save($"{path}/Pattern.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonNoAntialias() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.FillPolygon( - new GraphicsOptions(false), - Rgba32.HotPink, - simplePath)); - image.Save($"{path}/Simple_NoAntialias.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.True(Rgba32.HotPink == sourcePixels[11, 11], "[11, 11] wrong"); - - Assert.True(Rgba32.HotPink == sourcePixels[199, 149], "[199, 149] wrong"); - - Assert.True(Rgba32.HotPink == sourcePixels[50, 50], "[50, 50] wrong"); - - Assert.True(Rgba32.Blue == sourcePixels[2, 2], "[2, 2] wrong"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonImage() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - - using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateRgba32Image()) - using (var image = new Image(500, 500)) - { - var brush = new ImageBrush(brushImage); - - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.FillPolygon(brush, simplePath)); - image.Save($"{path}/Image.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonOpacity() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new SixLabors.Primitives.PointF[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }; - var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.FillPolygon(color, simplePath)); - image.Save($"{path}/Opacity.png"); - - //shift background color towards forground color by the opacity amount - var mergedColor = new Rgba32( - Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledRectangle() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - using (var image = new Image(500, 500)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.Fill( - Rgba32.HotPink, - new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140))); - image.Save($"{path}/Rectangle.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); - - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledTriangle() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - using (var image = new Image(100, 100)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate( - x => x.Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30))); - image.Save($"{path}/Triangle.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - Assert.Equal(Rgba32.Blue, sourcePixels[30, 65]); - - Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledSeptagon() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - var config = Configuration.CreateDefaultInstance(); - config.MaxDegreeOfParallelism = 1; - using (var image = new Image(config, 100, 100)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x.Fill(Rgba32.HotPink, - new RegularPolygon(50, 50, 7, 30, -(float)Math.PI))); - image.Save($"{path}/Septagon.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedByFilledEllipse() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - var config = Configuration.CreateDefaultInstance(); - config.MaxDegreeOfParallelism = 1; - using (var image = new Image(config, 100, 100)) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - image.Mutate(x => x - .Fill(Rgba32.HotPink, new EllipsePolygon(50, 50, 30, 50) - .Rotate((float)(Math.PI / 3)))); - image.Save($"{path}/ellipse.png"); - } - } - - [Fact] - public void ImageShouldBeOverlayedBySquareWithCornerClipped() - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); - - var config = Configuration.CreateDefaultInstance(); - config.MaxDegreeOfParallelism = 1; - using (var image = new Image(config, 200, 200)) - { - image.Mutate(x => x - .Fill(Rgba32.Blue) - .FillPolygon(Rgba32.HotPink, new SixLabors.Primitives.PointF[] - { - new Vector2( 8, 8 ), - new Vector2( 64, 8 ), - new Vector2( 64, 64 ), - new Vector2( 120, 64 ), - new Vector2( 120, 120 ), - new Vector2( 8, 120 ) - })); - image.Save($"{path}/clipped-corner.png"); - } - } - } -} diff --git a/tests/Images/External b/tests/Images/External index 5036d47ec2..0edeb078b9 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 5036d47ec2393d90efb6f3864d828e2e4381947f +Subproject commit 0edeb078b9d9f9c8be016ca514a3a625c4768bd6