// 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( Configuration configuration, GraphicsOptions options, ImageFrame source, RectangleF region) => new LinearGradientBrushApplicator( configuration, options, source, this.p1, this.p2, this.ColorStops, this.RepetitionMode); /// /// 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 configuration instance to use when performing operations. /// The graphics options. /// The source image. /// The start point of the gradient. /// The end point of the gradient. /// A tuple list of colors and their respective position between 0 and 1 on the line. /// Defines how the gradient colors are repeated. public LinearGradientBrushApplicator( Configuration configuration, GraphicsOptions options, ImageFrame source, PointF start, PointF end, ColorStop[] colorStops, GradientRepetitionMode repetitionMode) : base(configuration, options, source, 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 return distance / this.length; } } } } }