20 using System.Numerics;
21 using SixLabors.ImageSharp.Memory;
22 using SixLabors.ImageSharp.PixelFormats;
23 using SixLabors.ImageSharp.Drawing.Processing;
24 using SixLabors.ImageSharp;
25 using System.Reflection;
30 internal abstract class GradientBrushApplicator<TPixel> : BrushApplicator<TPixel>
31 where TPixel : unmanaged, IPixel<TPixel>
33 private static readonly TPixel Transparent = Color.Transparent.ToPixel<TPixel>();
35 private readonly ColorStop[] colorStops;
37 private readonly GradientRepetitionMode repetitionMode;
39 private readonly MemoryAllocator allocator;
41 private readonly
int scalineWidth;
43 private readonly
object blenderBuffers;
45 private bool isDisposed;
47 private PixelBlender<TPixel> _blender;
49 IMemoryOwner<float> amountBuffer;
50 IMemoryOwner<TPixel> overlayBuffer;
51 static Type constructedLocalBlenderBuffersType;
53 static GradientBrushApplicator()
55 Assembly utilities = Assembly.Load(
"SixLabors.ImageSharp.Drawing");
56 Type genericThreadLocalBlenderBuffersType = utilities.GetType(
"SixLabors.ImageSharp.Drawing.Utilities.ThreadLocalBlenderBuffers`1",
true);
58 Type[] typeArgs =
new Type[] { typeof(TPixel) };
59 constructedLocalBlenderBuffersType = genericThreadLocalBlenderBuffersType.MakeGenericType(typeArgs);
71 protected GradientBrushApplicator(
72 Configuration configuration,
73 GraphicsOptions options,
74 ImageFrame<TPixel> target,
75 ColorStop[] colorStops,
76 GradientRepetitionMode repetitionMode)
77 : base(configuration, options, target)
81 this.colorStops = colorStops;
82 this.repetitionMode = repetitionMode;
83 this.scalineWidth = target.Width;
84 this.allocator = configuration.MemoryAllocator;
86 this.blenderBuffers = Activator.CreateInstance(constructedLocalBlenderBuffersType,
new object[] { this.allocator, this.scalineWidth,
false });
88 this._blender = (PixelBlender<TPixel>)this.GetType().GetProperty(
"Blender", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty).GetValue(
this);
90 object data = this.blenderBuffers.GetType().GetField(
"data", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(this.blenderBuffers);
91 object bufferOwner = data.GetType().GetProperty(
"Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty).GetValue(data);
92 amountBuffer = (IMemoryOwner<float>)bufferOwner.GetType().GetField(
"amountBuffer", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(bufferOwner);
93 overlayBuffer = (IMemoryOwner<TPixel>)bufferOwner.GetType().GetField(
"overlayBuffer", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(bufferOwner);
96 internal TPixel
this[
int x,
int y]
100 float positionOnCompleteGradient = this.PositionOnGradient(x + 0.5f, y + 0.5f);
102 switch (this.repetitionMode)
104 case GradientRepetitionMode.None:
108 case GradientRepetitionMode.Repeat:
109 positionOnCompleteGradient %= 1;
111 case GradientRepetitionMode.Reflect:
112 positionOnCompleteGradient %= 2;
113 if (positionOnCompleteGradient > 1)
115 positionOnCompleteGradient = 2 - positionOnCompleteGradient;
119 case GradientRepetitionMode.DontFill:
120 if (positionOnCompleteGradient > 1 || positionOnCompleteGradient < 0)
127 throw new ArgumentOutOfRangeException();
130 (ColorStop from, ColorStop to) = this.GetGradientSegment(positionOnCompleteGradient);
132 if (from.Color.Equals(to.Color))
134 return from.Color.ToPixel<TPixel>();
137 float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / (to.Ratio - from.Ratio);
139 return new Color(Vector4.Lerp((Vector4)from.Color, (Vector4)to.Color, onLocalGradient)).ToPixel<TPixel>();
144 public override void Apply(Span<float> scanline,
int x,
int y)
146 Span<float> amounts = amountBuffer.Memory.Span.Slice(0, scanline.Length);
147 Span<TPixel> overlays = overlayBuffer.Memory.Span.Slice(0, scanline.Length);
149 float blendPercentage = this.Options.BlendPercentage;
152 if (blendPercentage < 1)
154 for (
int i = 0; i < scanline.Length; i++)
156 amounts[i] = scanline[i] * blendPercentage;
157 overlays[i] =
this[x + i, y];
162 for (
int i = 0; i < scanline.Length; i++)
164 amounts[i] = scanline[i];
165 overlays[i] =
this[x + i, y];
169 Span<TPixel> destinationRow = this.Target.PixelBuffer.DangerousGetRowSpan(y).Slice(x, scanline.Length);
171 this._blender.Blend(this.Configuration, destinationRow, destinationRow, overlays, amounts);
186 protected abstract float PositionOnGradient(
float x,
float y);
189 protected override void Dispose(
bool disposing)
196 base.Dispose(disposing);
200 ((IDisposable)this.blenderBuffers).Dispose();
203 this.isDisposed =
true;
206 private (ColorStop From, ColorStop To) GetGradientSegment(
float positionOnCompleteGradient)
208 ColorStop localGradientFrom = this.colorStops[0];
209 ColorStop localGradientTo =
default;
212 foreach (ColorStop colorStop
in this.colorStops)
214 localGradientTo = colorStop;
216 if (colorStop.Ratio > positionOnCompleteGradient)
222 localGradientFrom = localGradientTo;
225 return (localGradientFrom, localGradientTo);