diff --git a/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush.cs b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush.cs
new file mode 100644
index 0000000000..60040ab3c7
--- /dev/null
+++ b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/GradientBrushes/RadialGradientBrush.cs
@@ -0,0 +1,100 @@
+using System;
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes
+{
+ ///
+ /// A Circular Gradient Brush, defined by center point and radius.
+ ///
+ /// The pixel format.
+ public class RadialGradientBrush : AbstractGradientBrush
+ where TPixel : struct, IPixel
+ {
+ private readonly Point center;
+
+ private readonly float radius;
+
+ ///
+ /// The center of the circular gradient and 0 for the color stops.
+ /// The radius of the circular gradient and 1 for the color stops.
+ /// the color stops as defined in base class.
+ public RadialGradientBrush(
+ Point center,
+ float radius,
+ params ColorStop[] colorStops)
+ : base(colorStops)
+ {
+ this.center = center;
+ this.radius = radius;
+ }
+
+ ///
+ public override BrushApplicator CreateApplicator(
+ ImageFrame source,
+ RectangleF region,
+ GraphicsOptions options) =>
+ new RadialGradientBrushApplicator(
+ source,
+ options,
+ this.center,
+ this.radius,
+ this.ColorStops,
+ region);
+
+ ///
+ protected class RadialGradientBrushApplicator : AbstractGradientBrushApplicator
+ {
+ private readonly Point center;
+
+ private readonly float radius;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The target image
+ /// The options.
+ /// Center point of the gradient.
+ /// Radius of the gradient.
+ /// Definition of colors.
+ /// TODO !
+ public RadialGradientBrushApplicator(
+ ImageFrame target,
+ GraphicsOptions options,
+ Point center,
+ float radius,
+ ColorStop[] colorStops,
+ RectangleF region)
+ : base(target, options, colorStops, region)
+ {
+ this.center = center;
+ this.radius = radius;
+ }
+
+ ///
+ public override void Dispose()
+ {
+ }
+
+ ///
+ /// As this is a circular gradient, the position on the gradient is based on
+ /// the distance of the point to the center.
+ ///
+ /// The X coordinate of the target pixel.
+ /// The Y coordinate of the target pixel.
+ /// the position on the color gradient.
+ protected override float PositionOnGradient(int x, int y)
+ {
+ float distance = (float)Math.Sqrt(Math.Pow(this.center.X - x, 2) + Math.Pow(this.center.Y - y, 2));
+ return distance / this.radius;
+ }
+
+ internal override void Apply(Span scanline, int x, int y)
+ {
+ // TODO: each row is symmetric across center, so we can calculate half of it and mirror it to improve performance.
+ base.Apply(scanline, x, y);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs
new file mode 100644
index 0000000000..24a36a2524
--- /dev/null
+++ b/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs
@@ -0,0 +1,87 @@
+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 FillRadialGradientBrushTests : FileTestBase
+ {
+ [Fact]
+ public void RadialGradientBrushWithEqualColorsReturnsUnicolorImage()
+ {
+ string path = TestEnvironment.CreateOutputDirectory("Fill", "RadialGradientBrush");
+ using (var image = new Image(200, 200))
+ {
+ RadialGradientBrush unicolorRadialGradientBrush =
+ new RadialGradientBrush(
+ new SixLabors.Primitives.Point(0, 0),
+ 100,
+ new ColorStop(0, Rgba32.Red),
+ new ColorStop(1, Rgba32.Red));
+
+ image.Mutate(x => x.Fill(unicolorRadialGradientBrush));
+ image.Save($"{path}/UnicolorGradient.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(250, 250)]
+ [InlineData(0, 0)]
+ [InlineData(250, 0)]
+ [InlineData(0, 250)]
+ [InlineData(-100, 250)]
+ public void RadialGradientBrushWithDifferentCentersReturnsImage(
+ int centerX,
+ int centerY)
+ {
+ int width = 500;
+
+ string path = TestEnvironment.CreateOutputDirectory("Fill", "RadialGradientBrush");
+ using (var image = new Image(width, width))
+ {
+ RadialGradientBrush brush =
+ new RadialGradientBrush(
+ new SixLabors.Primitives.Point(centerX, centerY),
+ width / 2f,
+ new ColorStop(0, Rgba32.Red),
+ new ColorStop(1, Rgba32.Yellow));
+
+ image.Mutate(x => x.Fill(brush));
+ image.Save($"{path}/CenterAt{centerX}_{centerY}.png");
+
+ // using (PixelAccessor sourcePixels = image.Lock())
+ // {
+ // Rgba32 columnColor0 = sourcePixels[0, 0];
+ // Rgba32 columnColor23 = sourcePixels[23, 0];
+ // Rgba32 columnColor42 = sourcePixels[42, 0];
+ // Rgba32 columnColor333 = sourcePixels[333, 0];
+ //
+ // Rgba32 lastColumnColor = sourcePixels[lastColumnIndex, 0];
+ //
+ // for (int i = 0; i < width; i++)
+ // {
+ // // check first and last column:
+ // Assert.Equal(columnColor0, sourcePixels[0, i]);
+ // Assert.Equal(lastColumnColor, sourcePixels[lastColumnIndex, i]);
+ //
+ // // check the random colors:
+ // Assert.True(columnColor23 == sourcePixels[23, i], $"at {i}");
+ // Assert.Equal(columnColor42, sourcePixels[42, i]);
+ // Assert.Equal(columnColor333, sourcePixels[333, i]);
+ // }
+ // }
+ }
+ }
+ }
+}
\ No newline at end of file