diff --git a/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/EllipticGradientBrush.cs b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/EllipticGradientBrush.cs
new file mode 100644
index 000000000..21b581397
--- /dev/null
+++ b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/EllipticGradientBrush.cs
@@ -0,0 +1,143 @@
+using System;
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
+{
+ ///
+ /// 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.
+ ///
+ /// The Pixel format that is used.
+ public class EllipticGradientBrush : AbstractGradientBrush
+ where TPixel : struct, IPixel
+ {
+ private readonly Point center;
+
+ private readonly Point 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.
+ ///
+ /// the color stops as defined in base class.
+ public EllipticGradientBrush(
+ Point center,
+ Point referenceAxisEnd,
+ float axisRatio,
+ params ColorStop[] colorStops)
+ : base(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,
+ region);
+
+ ///
+ protected class RadialGradientBrushApplicator : AbstractGradientBrushApplicator
+ {
+ private readonly Point center;
+
+ private readonly Point referenceAxisEnd;
+
+ private readonly float axisRatio;
+
+ private readonly double rotation;
+
+ private readonly float referenceRadius;
+
+ private readonly float secondRadius;
+
+ ///
+ /// 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
+ /// TODO !
+ public RadialGradientBrushApplicator(
+ ImageFrame target,
+ GraphicsOptions options,
+ Point center,
+ Point referenceAxisEnd,
+ float axisRatio,
+ ColorStop[] colorStops,
+ RectangleF region)
+ : base(target, options, colorStops, region)
+ {
+ 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;
+ }
+
+ ///
+ public override void Dispose()
+ {
+ }
+
+ ///
+ protected override float PositionOnGradient(int xt, int yt)
+ {
+ float x0 = xt - this.center.X; // TODO: rotate this point after translation
+ float y0 = yt - this.center.Y;
+
+ float x = (float)((x0 * Math.Cos(this.rotation)) - (y0 * Math.Sin(this.rotation))); // TODO: store sin and cos of rotation as constant!
+ float y = (float)((x0 * Math.Sin(this.rotation)) + (y0 * Math.Cos(this.rotation)));
+
+ var inBoundaryChecker = ((x * x) / (this.referenceRadius * this.referenceRadius))
+ + ((y * y) / (this.secondRadius * this.secondRadius));
+
+ return inBoundaryChecker;
+ }
+
+ private float AngleBetween(PointF junction, PointF a, PointF b)
+ {
+ var vA = a - junction;
+ var vB = b - junction;
+ return (float)(Math.Atan2(vB.Y, vB.X)
+ - Math.Atan2(vA.Y, vA.X));
+ }
+
+ private float DistanceBetween(
+ PointF p1,
+ PointF p2)
+ {
+ return (float)Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs b/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs
new file mode 100644
index 000000000..c789e3e46
--- /dev/null
+++ b/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs
@@ -0,0 +1,124 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using SixLabors.ImageSharp.Processing.Drawing;
+using SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes;
+
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Drawing
+{
+ public class FillEllipticGradientBrushTests : FileTestBase
+ {
+ [Fact]
+ public void EllipticGradientBrushWithEqualColorsAndReturnsUnicolorImage()
+ {
+ string path = TestEnvironment.CreateOutputDirectory("Fill", "EllipticGradientBrush");
+ using (var image = new Image(10, 10))
+ {
+ EllipticGradientBrush unicolorLinearGradientBrush =
+ new EllipticGradientBrush(
+ new SixLabors.Primitives.Point(0, 0),
+ new SixLabors.Primitives.Point(10, 0),
+ 1.0f,
+ new ColorStop(0, Rgba32.Red),
+ new ColorStop(1, Rgba32.Red));
+
+ image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
+ image.Save($"{path}/UnicolorCircleGradient.png");
+
+ using (PixelAccessor sourcePixels = image.Lock())
+ {
+ Assert.Equal(Rgba32.Red, sourcePixels[0, 0]);
+ Assert.Equal(Rgba32.Red, sourcePixels[9, 9]);
+ Assert.Equal(Rgba32.Red, sourcePixels[5, 5]);
+ Assert.Equal(Rgba32.Red, sourcePixels[3, 8]);
+ }
+ }
+ }
+
+ [Theory]
+ [InlineData(0.1)]
+ [InlineData(0.4)]
+ [InlineData(0.8)]
+ [InlineData(1.0)]
+ [InlineData(1.2)]
+ [InlineData(1.6)]
+ [InlineData(2.0)]
+ public void EllipticGradientBrushProducesAxisParallelEllipsesWithDifferentRatio(
+ float ratio)
+ {
+ string path = TestEnvironment.CreateOutputDirectory("Fill", "EllipticGradientBrush");
+ using (var image = new Image(1000, 1000))
+ {
+ EllipticGradientBrush unicolorLinearGradientBrush =
+ new EllipticGradientBrush(
+ new SixLabors.Primitives.Point(500, 500),
+ new SixLabors.Primitives.Point(500, 750),
+ ratio,
+ new ColorStop(0, Rgba32.Yellow),
+ new ColorStop(1, Rgba32.Red),
+ new ColorStop(1, Rgba32.Black));
+
+ image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
+ image.Save($"{path}/Ellipsis{ratio}.png");
+ }
+ }
+
+ [Theory]
+ [InlineData(0.1, 0)]
+ [InlineData(0.4, 0)]
+ [InlineData(0.8, 0)]
+ [InlineData(1.0, 0)]
+
+ [InlineData(0.1, 45)]
+ [InlineData(0.4, 45)]
+ [InlineData(0.8, 45)]
+ [InlineData(1.0, 45)]
+
+ [InlineData(0.1, 90)]
+ [InlineData(0.4, 90)]
+ [InlineData(0.8, 90)]
+ [InlineData(1.0, 90)]
+
+ [InlineData(0.1, 30)]
+ [InlineData(0.4, 30)]
+ [InlineData(0.8, 30)]
+ [InlineData(1.0, 30)]
+ public void EllipticGradientBrushProducesRotatedEllipsesWithDifferentRatio(
+ float ratio,
+ float rotationInDegree)
+ {
+ var center = new SixLabors.Primitives.Point(500, 500);
+
+ var rotation = (Math.PI * rotationInDegree) / 180.0;
+ var cos = Math.Cos(rotation);
+ var sin = Math.Sin(rotation);
+
+ int axisX = (int)((center.X * cos) - (center.Y * sin));
+ int axisY = (int)((center.X * sin) + (center.Y * cos));
+
+ string path = TestEnvironment.CreateOutputDirectory("Fill", "EllipticGradientBrush");
+ using (var image = new Image(1000, 1000))
+ {
+ EllipticGradientBrush unicolorLinearGradientBrush =
+ new EllipticGradientBrush(
+ center,
+ new SixLabors.Primitives.Point(axisX, axisY),
+ ratio,
+ new ColorStop(0, Rgba32.Yellow),
+ new ColorStop(1, Rgba32.Red),
+ new ColorStop(1, Rgba32.Black));
+
+ image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
+ image.Save($"{path}/Ellipsis{ratio}_rot{rotationInDegree}°.png");
+ }
+ }
+ }
+}
\ No newline at end of file