// 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 { /// /// Gradient Brush with elliptic shape. /// The ellipse is defined by a center point, /// a point on the longest extension of the ellipse and /// the ratio between longest and shortest extension. /// public sealed class EllipticGradientBrush : GradientBrush { private readonly PointF center; private readonly PointF referenceAxisEnd; private readonly float axisRatio; /// /// The center of the elliptical gradient and 0 for the color stops. /// The end point of the reference axis of the ellipse. /// /// The ratio of the axis widths. /// The second axis' is perpendicular to the reference axis and /// it's length is the reference axis' length multiplied by this factor. /// /// Defines how the colors of the gradients are repeated. /// the color stops as defined in base class. public EllipticGradientBrush( PointF center, PointF referenceAxisEnd, float axisRatio, GradientRepetitionMode repetitionMode, params ColorStop[] colorStops) : base(repetitionMode, colorStops) { this.center = center; this.referenceAxisEnd = referenceAxisEnd; this.axisRatio = axisRatio; } /// public override BrushApplicator CreateApplicator( ImageFrame source, RectangleF region, GraphicsOptions options) => new RadialGradientBrushApplicator( source, options, this.center, this.referenceAxisEnd, this.axisRatio, this.ColorStops, this.RepetitionMode); /// private sealed class RadialGradientBrushApplicator : GradientBrushApplicator where TPixel : struct, IPixel { private readonly PointF center; private readonly PointF referenceAxisEnd; private readonly float axisRatio; private readonly double rotation; private readonly float referenceRadius; private readonly float secondRadius; private readonly float cosRotation; private readonly float sinRotation; private readonly float secondRadiusSquared; private readonly float referenceRadiusSquared; /// /// Initializes a new instance of the class. /// /// The target image /// The options /// Center of the ellipse /// Point on one angular points of the ellipse. /// /// Ratio of the axis length's. Used to determine the length of the second axis, /// the first is defined by and . /// Definition of colors /// Defines how the gradient colors are repeated. public RadialGradientBrushApplicator( ImageFrame target, GraphicsOptions options, PointF center, PointF referenceAxisEnd, float axisRatio, ColorStop[] colorStops, GradientRepetitionMode repetitionMode) : base(target, options, colorStops, repetitionMode) { this.center = center; this.referenceAxisEnd = referenceAxisEnd; this.axisRatio = axisRatio; this.rotation = this.AngleBetween( this.center, new PointF(this.center.X + 1, this.center.Y), this.referenceAxisEnd); this.referenceRadius = this.DistanceBetween(this.center, this.referenceAxisEnd); this.secondRadius = this.referenceRadius * this.axisRatio; this.referenceRadiusSquared = this.referenceRadius * this.referenceRadius; this.secondRadiusSquared = this.secondRadius * this.secondRadius; this.sinRotation = (float)Math.Sin(this.rotation); this.cosRotation = (float)Math.Cos(this.rotation); } /// public override void Dispose() { } /// protected override float PositionOnGradient(float xt, float yt) { float x0 = xt - this.center.X; float y0 = yt - this.center.Y; float x = (x0 * this.cosRotation) - (y0 * this.sinRotation); float y = (x0 * this.sinRotation) + (y0 * this.cosRotation); float xSquared = x * x; float ySquared = y * y; var inBoundaryChecker = (xSquared / this.referenceRadiusSquared) + (ySquared / this.secondRadiusSquared); return inBoundaryChecker; } private float AngleBetween(PointF junction, PointF a, PointF b) { var vA = a - junction; var vB = b - junction; return MathF.Atan2(vB.Y, vB.X) - MathF.Atan2(vA.Y, vA.X); } private float DistanceBetween( PointF p1, PointF p2) { float dX = p1.X - p2.X; float dXsquared = dX * dX; float dY = p1.Y - p2.Y; float dYsquared = dY * dY; return MathF.Sqrt(dXsquared + dYsquared); } } } }