Browse Source

implement radial gradient brush.

af/merge-core
Unknown 8 years ago
parent
commit
7548653547
  1. 100
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush.cs
  2. 87
      tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs

100
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
{
/// <summary>
/// A Circular Gradient Brush, defined by center point and radius.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class RadialGradientBrush<TPixel> : AbstractGradientBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly Point center;
private readonly float radius;
/// <inheritdoc cref="AbstractGradientBrush{TPixel}" />
/// <param name="center">The center of the circular gradient and 0 for the color stops.</param>
/// <param name="radius">The radius of the circular gradient and 1 for the color stops.</param>
/// <param name="colorStops">the color stops as defined in base class.</param>
public RadialGradientBrush(
Point center,
float radius,
params ColorStop<TPixel>[] colorStops)
: base(colorStops)
{
this.center = center;
this.radius = radius;
}
/// <inheritdoc cref="CreateApplicator" />
public override BrushApplicator<TPixel> CreateApplicator(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new RadialGradientBrushApplicator(
source,
options,
this.center,
this.radius,
this.ColorStops,
region);
/// <inheritdoc />
protected class RadialGradientBrushApplicator : AbstractGradientBrushApplicator
{
private readonly Point center;
private readonly float radius;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator" /> class.
/// </summary>
/// <param name="target">The target image</param>
/// <param name="options">The options.</param>
/// <param name="center">Center point of the gradient.</param>
/// <param name="radius">Radius of the gradient.</param>
/// <param name="colorStops">Definition of colors.</param>
/// <param name="region">TODO !</param>
public RadialGradientBrushApplicator(
ImageFrame<TPixel> target,
GraphicsOptions options,
Point center,
float radius,
ColorStop<TPixel>[] colorStops,
RectangleF region)
: base(target, options, colorStops, region)
{
this.center = center;
this.radius = radius;
}
/// <inheritdoc cref="Dispose" />
public override void Dispose()
{
}
/// <summary>
/// As this is a circular gradient, the position on the gradient is based on
/// the distance of the point to the center.
/// </summary>
/// <param name="x">The X coordinate of the target pixel.</param>
/// <param name="y">The Y coordinate of the target pixel.</param>
/// <returns>the position on the color gradient.</returns>
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<float> 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);
}
}
}
}

87
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<Rgba32>(200, 200))
{
RadialGradientBrush<Rgba32> unicolorRadialGradientBrush =
new RadialGradientBrush<Rgba32>(
new SixLabors.Primitives.Point(0, 0),
100,
new ColorStop<Rgba32>(0, Rgba32.Red),
new ColorStop<Rgba32>(1, Rgba32.Red));
image.Mutate(x => x.Fill(unicolorRadialGradientBrush));
image.Save($"{path}/UnicolorGradient.png");
using (PixelAccessor<Rgba32> 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<Rgba32>(width, width))
{
RadialGradientBrush<Rgba32> brush =
new RadialGradientBrush<Rgba32>(
new SixLabors.Primitives.Point(centerX, centerY),
width / 2f,
new ColorStop<Rgba32>(0, Rgba32.Red),
new ColorStop<Rgba32>(1, Rgba32.Yellow));
image.Mutate(x => x.Fill(brush));
image.Save($"{path}/CenterAt{centerX}_{centerY}.png");
// using (PixelAccessor<Rgba32> 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]);
// }
// }
}
}
}
}
Loading…
Cancel
Save