diff --git a/src/ImageSharp.Drawing/Processing/Drawing/Brushes/LinearGradientBrush.cs b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/LinearGradientBrush.cs index b955cd7504..8ef2295483 100644 --- a/src/ImageSharp.Drawing/Processing/Drawing/Brushes/LinearGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/LinearGradientBrush.cs @@ -105,19 +105,14 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes private readonly float acrossX; /// - /// helper to speed up calculation as these dont't change + /// the result of ^2 + ^2 /// - private readonly float aYcX; + private readonly float alongsSquared; /// - /// helper to speed up calculation as these dont't change + /// the length of the defined gradient (between source and end) /// - private readonly float aXcY; - - /// - /// helper to speed up calculation as these dont't change - /// - private readonly float aXcX; + private readonly float length; /// /// Initializes a new instance of the 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); } /// @@ -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 scanline, int x, int y) diff --git a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs index d1b253680e..52979d792b 100644 --- a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs +++ b/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(size, size)) + { + LinearGradientBrush unicolorLinearGradientBrush = + new LinearGradientBrush( + new SixLabors.Primitives.Point(startX, startY), + new SixLabors.Primitives.Point(endX, endY), + new LinearGradientBrush.ColorStop(0, Rgba32.Red), + new LinearGradientBrush.ColorStop(1, Rgba32.Yellow)); + + image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); + image.Save($"{path}/diagonalRedToYellowFrom{startX}_{startY}.png"); + + using (PixelAccessor 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]); + + } + } + } } } \ No newline at end of file