// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
///
/// Provides an implementation of a brush for painting linear gradients within areas.
/// Supported right now:
/// - a set of colors in relative distances to each other.
///
public sealed class LinearGradientBrush : GradientBrush
{
private readonly PointF p1;
private readonly PointF p2;
///
/// Initializes a new instance of the class.
///
/// Start point
/// End point
/// defines how colors are repeated.
///
public LinearGradientBrush(
PointF p1,
PointF p2,
GradientRepetitionMode repetitionMode,
params ColorStop[] colorStops)
: base(repetitionMode, colorStops)
{
this.p1 = p1;
this.p2 = p2;
}
///
public override BrushApplicator CreateApplicator(
ImageFrame source,
RectangleF region,
GraphicsOptions options) =>
new LinearGradientBrushApplicator(
source,
this.p1,
this.p2,
this.ColorStops,
this.RepetitionMode,
options);
///
/// The linear gradient brush applicator.
///
private sealed class LinearGradientBrushApplicator : GradientBrushApplicator
where TPixel : struct, IPixel
{
private readonly PointF start;
private readonly PointF end;
///
/// the vector along the gradient, x component
///
private readonly float alongX;
///
/// the vector along the gradient, y component
///
private readonly float alongY;
///
/// the vector perpendicular to the gradient, y component
///
private readonly float acrossY;
///
/// the vector perpendicular to the gradient, x component
///
private readonly float acrossX;
///
/// the result of ^2 + ^2
///
private readonly float alongsSquared;
///
/// the length of the defined gradient (between source and end)
///
private readonly float length;
///
/// Initializes a new instance of the class.
///
/// The source
/// start point of the gradient
/// end point of the gradient
/// tuple list of colors and their respective position between 0 and 1 on the line
/// defines how the gradient colors are repeated.
/// the graphics options
public LinearGradientBrushApplicator(
ImageFrame source,
PointF start,
PointF end,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode,
GraphicsOptions options)
: base(source, options, colorStops, repetitionMode)
{
this.start = start;
this.end = end;
// the along vector:
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.alongsSquared = (this.alongX * this.alongX) + (this.alongY * this.alongY);
this.length = MathF.Sqrt(this.alongsSquared);
}
protected override float PositionOnGradient(float x, float y)
{
if (this.acrossX == 0)
{
return (x - this.start.X) / (this.end.X - this.start.X);
}
else if (this.acrossY == 0)
{
return (y - this.start.Y) / (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 = MathF.Sqrt(MathF.Pow(x4 - this.start.X, 2) + MathF.Pow(y4 - this.start.Y, 2));
// get and return ratio
float ratio = distance / this.length;
return ratio;
}
}
public override void Dispose()
{
}
}
}
}