From 75486535478e354d3c5cea945741fd1c6a9e5edc Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 22 Apr 2018 14:16:24 +0200 Subject: [PATCH] implement radial gradient brush. --- .../GradientBrushes/RadialGradientBrush.cs | 100 ++++++++++++++++++ .../Drawing/FillRadialGradientBrushTests.cs | 87 +++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush.cs create mode 100644 tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs diff --git a/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush.cs b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush.cs new file mode 100644 index 0000000000..60040ab3c7 --- /dev/null +++ b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush.cs @@ -0,0 +1,100 @@ +using System; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes +{ + /// + /// A Circular Gradient Brush, defined by center point and radius. + /// + /// The pixel format. + public class RadialGradientBrush : AbstractGradientBrush + where TPixel : struct, IPixel + { + private readonly Point center; + + private readonly float radius; + + /// + /// The center of the circular gradient and 0 for the color stops. + /// The radius of the circular gradient and 1 for the color stops. + /// the color stops as defined in base class. + public RadialGradientBrush( + Point center, + float radius, + params ColorStop[] colorStops) + : base(colorStops) + { + this.center = center; + this.radius = radius; + } + + /// + public override BrushApplicator CreateApplicator( + ImageFrame source, + RectangleF region, + GraphicsOptions options) => + new RadialGradientBrushApplicator( + source, + options, + this.center, + this.radius, + this.ColorStops, + region); + + /// + protected class RadialGradientBrushApplicator : AbstractGradientBrushApplicator + { + private readonly Point center; + + private readonly float radius; + + /// + /// Initializes a new instance of the class. + /// + /// The target image + /// The options. + /// Center point of the gradient. + /// Radius of the gradient. + /// Definition of colors. + /// TODO ! + public RadialGradientBrushApplicator( + ImageFrame target, + GraphicsOptions options, + Point center, + float radius, + ColorStop[] colorStops, + RectangleF region) + : base(target, options, colorStops, region) + { + this.center = center; + this.radius = radius; + } + + /// + public override void Dispose() + { + } + + /// + /// As this is a circular gradient, the position on the gradient is based on + /// the distance of the point to the center. + /// + /// The X coordinate of the target pixel. + /// The Y coordinate of the target pixel. + /// the position on the color gradient. + protected override float PositionOnGradient(int x, int y) + { + float distance = (float)Math.Sqrt(Math.Pow(this.center.X - x, 2) + Math.Pow(this.center.Y - y, 2)); + return distance / this.radius; + } + + internal override void Apply(Span scanline, int x, int y) + { + // TODO: each row is symmetric across center, so we can calculate half of it and mirror it to improve performance. + base.Apply(scanline, x, y); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs new file mode 100644 index 0000000000..24a36a2524 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs @@ -0,0 +1,87 @@ +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Drawing; +using SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + public class FillRadialGradientBrushTests : FileTestBase + { + [Fact] + public void RadialGradientBrushWithEqualColorsReturnsUnicolorImage() + { + string path = TestEnvironment.CreateOutputDirectory("Fill", "RadialGradientBrush"); + using (var image = new Image(200, 200)) + { + RadialGradientBrush unicolorRadialGradientBrush = + new RadialGradientBrush( + new SixLabors.Primitives.Point(0, 0), + 100, + new ColorStop(0, Rgba32.Red), + new ColorStop(1, Rgba32.Red)); + + image.Mutate(x => x.Fill(unicolorRadialGradientBrush)); + image.Save($"{path}/UnicolorGradient.png"); + + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Rgba32.Red, sourcePixels[0, 0]); + Assert.Equal(Rgba32.Red, sourcePixels[9, 9]); + Assert.Equal(Rgba32.Red, sourcePixels[5, 5]); + Assert.Equal(Rgba32.Red, sourcePixels[3, 8]); + } + } + } + + [Theory] + [InlineData(250, 250)] + [InlineData(0, 0)] + [InlineData(250, 0)] + [InlineData(0, 250)] + [InlineData(-100, 250)] + public void RadialGradientBrushWithDifferentCentersReturnsImage( + int centerX, + int centerY) + { + int width = 500; + + string path = TestEnvironment.CreateOutputDirectory("Fill", "RadialGradientBrush"); + using (var image = new Image(width, width)) + { + RadialGradientBrush brush = + new RadialGradientBrush( + new SixLabors.Primitives.Point(centerX, centerY), + width / 2f, + new ColorStop(0, Rgba32.Red), + new ColorStop(1, Rgba32.Yellow)); + + image.Mutate(x => x.Fill(brush)); + image.Save($"{path}/CenterAt{centerX}_{centerY}.png"); + + // using (PixelAccessor sourcePixels = image.Lock()) + // { + // Rgba32 columnColor0 = sourcePixels[0, 0]; + // Rgba32 columnColor23 = sourcePixels[23, 0]; + // Rgba32 columnColor42 = sourcePixels[42, 0]; + // Rgba32 columnColor333 = sourcePixels[333, 0]; + // + // Rgba32 lastColumnColor = sourcePixels[lastColumnIndex, 0]; + // + // for (int i = 0; i < width; i++) + // { + // // check first and last column: + // Assert.Equal(columnColor0, sourcePixels[0, i]); + // Assert.Equal(lastColumnColor, sourcePixels[lastColumnIndex, i]); + // + // // check the random colors: + // Assert.True(columnColor23 == sourcePixels[23, i], $"at {i}"); + // Assert.Equal(columnColor42, sourcePixels[42, i]); + // Assert.Equal(columnColor333, sourcePixels[333, i]); + // } + // } + } + } + } +} \ No newline at end of file