mirror of https://github.com/SixLabors/ImageSharp
2 changed files with 187 additions and 0 deletions
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue