Browse Source

#542: use struct for ColorStops

as proposed by @antonfirsov: improving readability and memory locality.
af/merge-core
Unknown 8 years ago
parent
commit
99ed4586ad
  1. 59
      src/ImageSharp.Drawing/Processing/Drawing/Brushes/LinearGradientBrush.cs
  2. 4
      tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs

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

@ -19,28 +19,59 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
public class LinearGradientBrush<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// A struct that defines a single color stop.
/// </summary>
public struct ColorStop
{
/// <summary>
/// Create a new ColorStop
/// </summary>
/// <param name="ratio">Where should it be? 0 is at the start, 1 at the end of the <see cref="LinearGradientBrush{TPixel}"/>.</param>
/// <param name="color">What color should be used at that point?</param>
public ColorStop(float ratio, TPixel color)
{
this.Ratio = ratio;
this.Color = color;
}
/// <summary>
/// The point along the defined <see cref="LinearGradientBrush{TPixel}" /> gradient axis.
/// </summary>
public float Ratio { get; }
/// <summary>
/// The color to be used.
/// </summary>
public TPixel Color { get; }
}
private readonly Point p1;
private readonly Point p2;
private readonly Tuple<float, TPixel>[] keyColors;
private readonly ColorStop[] colorStops;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrush{TPixel}"/> class.
/// </summary>
/// <param name="p1">Start point</param>
/// <param name="p2">End point</param>
/// <param name="keyColors">a set of color keys and where they are. The double must be in range [0..1] and is relative between p1 and p2.</param>
public LinearGradientBrush(Point p1, Point p2, params Tuple<float, TPixel>[] keyColors)
/// <param name="colorStops">
/// A set of color keys and where they are.
/// The double should be in range [0..1] and is relative between p1 and p2.
/// TODO: what about the [0..1] restriction? is it necessary? If so, it should be checked, if not, it should be explained what happens for greater/smaller values.
/// </param>
public LinearGradientBrush(Point p1, Point p2, params ColorStop[] colorStops)
{
this.p1 = p1;
this.p2 = p2;
this.keyColors = keyColors;
this.colorStops = colorStops;
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
=> new LinearGradientBrushApplicator(source, this.p1, this.p2, this.keyColors, region, options);
=> new LinearGradientBrushApplicator(source, this.p1, this.p2, this.colorStops, region, options);
/// <summary>
/// The linear gradient brush applicator.
@ -51,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
private readonly Point end;
private readonly Tuple<float, TPixel>[] colorStops;
private readonly ColorStop[] colorStops;
/// <summary>
/// the vector along the gradient, x component
@ -101,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
ImageFrame<TPixel> source,
Point start,
Point end,
Tuple<float, TPixel>[] colorStops,
ColorStop[] colorStops,
RectangleF region, // TODO: use region, compare with other Brushes for reference.
GraphicsOptions options)
: base(source, options)
@ -138,13 +169,13 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
float onCompleteGradient = this.RatioOnGradient(x, y);
var localGradientFrom = this.colorStops[0];
Tuple<float, TPixel> localGradientTo = null;
ColorStop localGradientTo = default;
// TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient)
foreach (var colorStop in this.colorStops)
{
localGradientTo = colorStop;
if (colorStop.Item1 >= onCompleteGradient)
if (colorStop.Ratio >= onCompleteGradient)
{
// we're done here, so break it!
break;
@ -154,15 +185,15 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Brushes
}
TPixel resultColor = default;
if (localGradientFrom.Item2.Equals(localGradientTo.Item2))
if (localGradientFrom.Color.Equals(localGradientTo.Color))
{
resultColor = localGradientFrom.Item2;
resultColor = localGradientFrom.Color;
}
else
{
var fromAsVector = localGradientFrom.Item2.ToVector4();
var toAsVector = localGradientTo.Item2.ToVector4();
float onLocalGradient = (onCompleteGradient - localGradientFrom.Item1) / localGradientTo.Item1; // TODO:
var fromAsVector = localGradientFrom.Color.ToVector4();
var toAsVector = localGradientTo.Color.ToVector4();
float onLocalGradient = (onCompleteGradient - localGradientFrom.Ratio) / localGradientTo.Ratio; // TODO:
Vector4 result = PorterDuffFunctions.Normal(
fromAsVector,

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

@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new LinearGradientBrush<Rgba32>(
new SixLabors.Primitives.Point(0, 0),
new SixLabors.Primitives.Point(500, 0),
new Tuple<float, Rgba32>(0, Rgba32.Red),
new Tuple<float, Rgba32>(1, Rgba32.Red));
new LinearGradientBrush<Rgba32>.ColorStop(0, Rgba32.Red),
new LinearGradientBrush<Rgba32>.ColorStop(1, Rgba32.Red));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
image.Save($"{path}/UnicolorGradient.png");

Loading…
Cancel
Save