// 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. /// /// The pixel format public sealed class LinearGradientBrush : GradientBrushBase where TPixel : struct, IPixel { private readonly Point p1; private readonly Point p2; /// /// Initializes a new instance of the class. /// /// Start point /// End point /// defines how colors are repeated. /// public LinearGradientBrush( Point p1, Point 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 : GradientBrushApplicatorBase { private readonly Point start; private readonly Point 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, Point start, Point 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 = (float)Math.Sqrt(this.alongsSquared); } protected override float PositionOnGradient(int x, int y) { 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; } } public override void Dispose() { } } } }