Browse Source

#542: fix implementation of non-axial gradients and add tests

the theory lacks color checks yet that should be added, but it produces output images for visual control
af/merge-core
Unknown 8 years ago
parent
commit
249b1189cc
  1. 51
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/LinearGradientBrush.cs
  2. 35
      tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs

51
src/ImageSharp.Drawing/Processing/Drawing/Brushes/LinearGradientBrush.cs

@ -105,19 +105,14 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
private readonly float acrossX;
/// <summary>
/// helper to speed up calculation as these dont't change
/// the result of <see cref="alongX"/>^2 + <see cref="alongY"/>^2
/// </summary>
private readonly float aYcX;
private readonly float alongsSquared;
/// <summary>
/// helper to speed up calculation as these dont't change
/// the length of the defined gradient (between source and end)
/// </summary>
private readonly float aXcY;
/// <summary>
/// helper to speed up calculation as these dont't change
/// </summary>
private readonly float aXcX;
private readonly float length;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrushApplicator" /> class.
@ -142,17 +137,16 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
this.colorStops = colorStops; // TODO: requires colorStops to be sorted by Item1!
// the along vector:
this.alongX = this.start.X - this.end.X;
this.alongY = this.start.Y - this.end.Y;
this.alongX = this.end.X - this.start.X;
this.alongY = this.end.Y - this.start.Y;
// the cross vector:
this.acrossX = this.alongY;
this.acrossY = -this.alongX;
// some helpers:
this.aYcX = this.alongY * this.acrossX;
this.aXcY = this.alongX * this.acrossY;
this.aXcX = this.alongX * this.acrossX;
this.alongsSquared = (this.alongX * this.alongX) + (this.alongY * this.alongY);
this.length = (float)Math.Sqrt(this.alongsSquared);
}
/// <summary>
@ -210,8 +204,33 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
private float RatioOnGradient(int x, int y)
{
return ((x / this.acrossX) - (this.alongX * y / this.aYcX))
/ (1 - (this.aXcY / this.aXcX));
if (this.acrossX == 0)
{
return (x - this.start.X) / (float)(this.end.X - this.start.X);
}
else if (this.acrossY == 0)
{
return (y - this.start.Y) / (float)(this.end.Y - this.start.Y);
}
else
{
float deltaX = x - this.start.X;
float deltaY = y - this.start.Y;
float k = ((this.alongY * deltaX) - (this.alongX * deltaY)) / this.alongsSquared;
// point on the line:
float x4 = x - (k * this.alongY);
float y4 = y + (k * this.alongX);
// get distance from (x4,y4) to start
float distance = (float)Math.Sqrt(
Math.Pow(x4 - this.start.X, 2)
+ Math.Pow(y4 - this.start.Y, 2));
// get and return ratio
float ratio = distance / this.length;
return ratio;
}
}
internal override void Apply(Span<float> scanline, int x, int y)

35
tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs

@ -123,5 +123,40 @@ namespace SixLabors.ImageSharp.Tests.Drawing
}
}
}
[Theory]
[InlineData(0, 0, 499, 499)]
[InlineData(0, 499, 499, 0)]
[InlineData(499, 499, 0, 0)]
[InlineData(499, 0, 0, 499)]
public void DiagonalLinearGradientBrushReturnsUnicolorColumns(
int startX, int startY, int endX, int endY)
{
int size = 500;
int lastIndex = size - 1;
string path = TestEnvironment.CreateOutputDirectory("Fill", "LinearGradientBrush");
using (var image = new Image<Rgba32>(size, size))
{
LinearGradientBrush<Rgba32> unicolorLinearGradientBrush =
new LinearGradientBrush<Rgba32>(
new SixLabors.Primitives.Point(startX, startY),
new SixLabors.Primitives.Point(endX, endY),
new LinearGradientBrush<Rgba32>.ColorStop(0, Rgba32.Red),
new LinearGradientBrush<Rgba32>.ColorStop(1, Rgba32.Yellow));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
image.Save($"{path}/diagonalRedToYellowFrom{startX}_{startY}.png");
using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{
// check first and last pixel, these are known:
Assert.Equal(Rgba32.Red, sourcePixels[startX, startY]);
Assert.Equal(Rgba32.Yellow, sourcePixels[endX, endY]);
}
}
}
}
}
Loading…
Cancel
Save