diff --git a/src/ImageSharp.Drawing/Brushes/IBrush.cs b/src/ImageSharp.Drawing/Brushes/IBrush.cs
index e16f22028..9534c7a88 100644
--- a/src/ImageSharp.Drawing/Brushes/IBrush.cs
+++ b/src/ImageSharp.Drawing/Brushes/IBrush.cs
@@ -24,6 +24,7 @@ namespace ImageSharp.Drawing
///
/// The pixel source.
/// The region the brush will be applied to.
+ /// The graphic options
///
/// The brush applicator for this brush
///
@@ -31,6 +32,6 @@ namespace ImageSharp.Drawing
/// The when being applied to things like shapes would usually be the
/// bounding box of the shape not necessarily the bounds of the whole image
///
- BrushApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region);
+ BrushApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region, GraphicsOptions options);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
index 3e2da040f..5b1409fe2 100644
--- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
@@ -31,9 +31,9 @@ namespace ImageSharp.Drawing.Brushes
}
///
- public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region)
+ public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options)
{
- return new ImageBrushApplicator(sourcePixels, this.image, region);
+ return new ImageBrushApplicator(sourcePixels, this.image, region, options);
}
///
@@ -57,9 +57,14 @@ namespace ImageSharp.Drawing.Brushes
private readonly int xLength;
///
- /// The offset.
+ /// The Y offset.
///
- private readonly Vector2 offset;
+ private readonly int offsetY;
+
+ ///
+ /// The X offset.
+ ///
+ private readonly int offsetX;
///
/// Initializes a new instance of the class.
@@ -70,16 +75,18 @@ namespace ImageSharp.Drawing.Brushes
///
/// The region.
///
+ /// The options
///
/// The sourcePixels.
///
- public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region)
- : base(sourcePixels)
+ public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region, GraphicsOptions options)
+ : base(sourcePixels, options)
{
this.source = image.Lock();
this.xLength = image.Width;
this.yLength = image.Height;
- this.offset = new Vector2(MathF.Max(MathF.Floor(region.Top), 0), MathF.Max(MathF.Floor(region.Left), 0));
+ this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0);
+ this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0);
}
///
@@ -94,13 +101,8 @@ namespace ImageSharp.Drawing.Brushes
{
get
{
- Vector2 point = new Vector2(x, y);
-
- // Offset the requested pixel by the value in the rectangle (the shapes position)
- point = point - this.offset;
- int srcX = (int)point.X % this.xLength;
- int srcY = (int)point.Y % this.yLength;
-
+ int srcX = (x - this.offsetX) % this.xLength;
+ int srcY = (y - this.offsetY) % this.yLength;
return this.source[srcX, srcY];
}
}
@@ -112,33 +114,27 @@ namespace ImageSharp.Drawing.Brushes
}
///
- internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ internal override void Apply(BufferSpan scanline, int x, int y)
{
- Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
-
- using (Buffer buffer = new Buffer(scanlineBuffer))
+ // create a span for colors
+ using (Buffer amountBuffer = new Buffer(scanline.Length))
+ using (Buffer overlay = new Buffer(scanline.Length))
{
- BufferSpan slice = buffer.Slice(offset);
+ int sourceY = (y - this.offsetY) % this.yLength;
+ int offsetX = x - this.offsetX;
+ BufferSpan sourceRow = this.source.GetRowSpan(sourceY);
- for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ for (int i = 0; i < scanline.Length; i++)
{
- int targetX = xPos + x;
- int targetY = y;
-
- float opacity = slice[xPos];
- if (opacity > Constants.Epsilon)
- {
- Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
-
- Vector4 sourceVector = this[targetX, targetY].ToVector4();
+ amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
-
- TPixel packed = default(TPixel);
- packed.PackFromVector4(finalColor);
- this.Target[targetX, targetY] = packed;
- }
+ int sourceX = (i + offsetX) % this.xLength;
+ TPixel pixel = sourceRow[sourceX];
+ overlay[i] = pixel;
}
+
+ BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
+ this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
index ad37f4d28..4aebe00fb 100644
--- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
@@ -90,9 +90,9 @@ namespace ImageSharp.Drawing.Brushes
}
///
- public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region)
+ public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options)
{
- return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector);
+ return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector, options);
}
///
@@ -112,8 +112,9 @@ namespace ImageSharp.Drawing.Brushes
/// The sourcePixels.
/// The pattern.
/// The patternVector.
- public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector)
- : base(sourcePixels)
+ /// The options
+ public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options)
+ : base(sourcePixels, options)
{
this.pattern = pattern;
this.patternVector = patternVector;
@@ -146,34 +147,22 @@ namespace ImageSharp.Drawing.Brushes
}
///
- internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ internal override void Apply(BufferSpan scanline, int x, int y)
{
- Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
-
- using (Buffer buffer = new Buffer(scanlineBuffer))
+ int patternY = y % this.pattern.Height;
+ using (Buffer amountBuffer = new Buffer(scanline.Length))
+ using (Buffer overlay = new Buffer(scanline.Length))
{
- BufferSpan slice = buffer.Slice(offset);
-
- for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ for (int i = 0; i < scanline.Length; i++)
{
- int targetX = xPos + x;
- int targetY = y;
-
- float opacity = slice[xPos];
- if (opacity > Constants.Epsilon)
- {
- Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
+ amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
- // 2d array index at row/column
- Vector4 sourceVector = this.patternVector[targetY % this.patternVector.Height, targetX % this.patternVector.Width];
-
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
-
- TPixel packed = default(TPixel);
- packed.PackFromVector4(finalColor);
- this.Target[targetX, targetY] = packed;
- }
+ int patternX = (x + i) % this.pattern.Width;
+ overlay[i] = this.pattern[y, x];
}
+
+ BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
+ this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
index 5dd6dad76..59cb0820a 100644
--- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
+++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
@@ -21,16 +21,31 @@ namespace ImageSharp.Drawing.Processors
/// Initializes a new instance of the class.
///
/// The target.
- internal BrushApplicator(PixelAccessor target)
+ /// The options.
+ internal BrushApplicator(PixelAccessor target, GraphicsOptions options)
{
this.Target = target;
+
+ this.Options = options;
+
+ this.Blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode);
}
+ ///
+ /// Gets the blendder
+ ///
+ internal PixelBlender Blender { get; }
+
///
/// Gets the destinaion
///
protected PixelAccessor Target { get; }
+ ///
+ /// Gets the blend percentage
+ ///
+ protected GraphicsOptions Options { get; private set; }
+
///
/// Gets the color for a single pixel.
///
@@ -45,39 +60,27 @@ namespace ImageSharp.Drawing.Processors
///
/// Applies the opactiy weighting for each pixel in a scanline to the target based on the pattern contained in the brush.
///
- /// The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.
- /// The number of pixels effected by this scanline.
- /// The offset fromthe begining of the opacity data starts.
+ /// The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.
/// The x position in the target pixel space that the start of the scanline data corresponds to.
/// The y position in the target pixel space that whole scanline corresponds to.
/// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.
- internal virtual void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ internal virtual void Apply(BufferSpan scanline, int x, int y)
{
- DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
-
- using (Buffer buffer = new Buffer(scanlineBuffer))
+ using (Buffer amountBuffer = new Buffer(scanline.Length))
+ using (Buffer overlay = new Buffer(scanline.Length))
{
- BufferSpan slice = buffer.Slice(offset);
-
- for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ for (int i = 0; i < scanline.Length; i++)
{
- int targetX = xPos + x;
- int targetY = y;
-
- float opacity = slice[xPos];
- if (opacity > Constants.Epsilon)
+ if (this.Options.BlendPercentage < 1)
{
- Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
-
- Vector4 sourceVector = this[targetX, targetY].ToVector4();
-
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
-
- TPixel packed = default(TPixel);
- packed.PackFromVector4(finalColor);
- this.Target[targetX, targetY] = packed;
+ amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
}
+
+ overlay[i] = this[x + i, y];
}
+
+ BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
+ this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
index 7c192b2d3..b8b2499ff 100644
--- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
@@ -54,9 +54,9 @@ namespace ImageSharp.Drawing.Brushes
public TPixel TargeTPixel { get; }
///
- public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region)
+ public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options)
{
- return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargeTPixel, this.Threshold);
+ return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargeTPixel, this.Threshold, options);
}
///
@@ -86,8 +86,9 @@ namespace ImageSharp.Drawing.Brushes
/// Color of the source.
/// Color of the target.
/// The threshold .
- public RecolorBrushApplicator(PixelAccessor sourcePixels, TPixel sourceColor, TPixel targeTPixel, float threshold)
- : base(sourcePixels)
+ /// The options
+ public RecolorBrushApplicator(PixelAccessor sourcePixels, TPixel sourceColor, TPixel targeTPixel, float threshold, GraphicsOptions options)
+ : base(sourcePixels, options)
{
this.sourceColor = sourceColor.ToVector4();
this.targeTPixel = targeTPixel.ToVector4();
@@ -136,42 +137,24 @@ namespace ImageSharp.Drawing.Brushes
}
///
- internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ internal override void Apply(BufferSpan scanline, int x, int y)
{
- Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
-
- using (Buffer buffer = new Buffer(scanlineBuffer))
+ using (Buffer amountBuffer = new Buffer(scanline.Length))
+ using (Buffer overlay = new Buffer(scanline.Length))
{
- BufferSpan slice = buffer.Slice(offset);
-
- for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ for (int i = 0; i < scanline.Length; i++)
{
- int targetX = xPos + x;
- int targetY = y;
-
- float opacity = slice[xPos];
- if (opacity > Constants.Epsilon)
- {
- Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
-
- Vector4 sourceVector = backgroundVector;
- float distance = Vector4.DistanceSquared(sourceVector, this.sourceColor);
- if (distance <= this.threshold)
- {
- float lerpAmount = (this.threshold - distance) / this.threshold;
- sourceVector = Vector4BlendTransforms.PremultipliedLerp(
- sourceVector,
- this.targeTPixel,
- lerpAmount);
-
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
-
- TPixel packed = default(TPixel);
- packed.PackFromVector4(finalColor);
- this.Target[targetX, targetY] = packed;
- }
- }
+ amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
+
+ int offsetX = x + i;
+
+ // no doubt this one can be optermised further but I can't imagine its
+ // actually being used and can probably be removed/interalised for now
+ overlay[i] = this[offsetX, y];
}
+
+ BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
+ this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
index 634a2b70d..f97266c77 100644
--- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
@@ -39,34 +39,61 @@ namespace ImageSharp.Drawing.Brushes
public TPixel Color => this.color;
///
- public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region)
+ public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options)
{
- return new SolidBrushApplicator(sourcePixels, this.color);
+ if (options.BlendPercentage < 0)
+ {
+ return new SolidBrushApplicator(sourcePixels, this.color, options);
+ }
+ else
+ {
+ return new SolidNoBlendBrushApplicator(sourcePixels, this.color, options);
+ }
}
///
/// The solid brush applicator.
///
- private class SolidBrushApplicator : BrushApplicator
+ private class SolidNoBlendBrushApplicator : SolidBrushApplicator
{
- ///
- /// The solid color.
- ///
- private readonly TPixel color;
- private readonly Vector4 colorVector;
+ public SolidNoBlendBrushApplicator(PixelAccessor sourcePixels, TPixel color, GraphicsOptions options)
+ : base(sourcePixels, color, options)
+ {
+ }
+ internal override void Apply(BufferSpan scanline, int x, int y)
+ {
+ BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
+ this.Blender.Compose(destinationRow, destinationRow, this.Colors, scanline);
+ }
+ }
+
+ ///
+ /// The solid brush applicator.
+ ///
+ private class SolidBrushApplicator : BrushApplicator
+ {
///
/// Initializes a new instance of the class.
///
/// The color.
+ /// The options
/// The sourcePixels.
- public SolidBrushApplicator(PixelAccessor sourcePixels, TPixel color)
- : base(sourcePixels)
+ public SolidBrushApplicator(PixelAccessor sourcePixels, TPixel color, GraphicsOptions options)
+ : base(sourcePixels, options)
{
- this.color = color;
- this.colorVector = color.ToVector4();
+ this.Colors = new Buffer(sourcePixels.Width);
+ for (int i = 0; i < this.Colors.Length; i++)
+ {
+ this.Colors[i] = color;
+ }
}
+ ///
+ /// Gets the colors.
+ ///
+ protected Buffer Colors { get; }
+
///
/// Gets the color for a single pixel.
///
@@ -75,41 +102,27 @@ namespace ImageSharp.Drawing.Brushes
///
/// The color
///
- internal override TPixel this[int x, int y] => this.color;
+ internal override TPixel this[int x, int y] => this.Colors[x];
///
public override void Dispose()
{
- // noop
+ this.Colors.Dispose();
}
///
- internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ internal override void Apply(BufferSpan scanline, int x, int y)
{
- Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
+ BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
- using (Buffer buffer = new Buffer(scanlineBuffer))
+ using (Buffer amountBuffer = new Buffer(scanline.Length))
{
- BufferSpan slice = buffer.Slice(offset);
-
- for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ for (int i = 0; i < scanline.Length; i++)
{
- int targetX = xPos + x;
- int targetY = y;
-
- float opacity = slice[xPos];
- if (opacity > Constants.Epsilon)
- {
- Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
- Vector4 sourceVector = this.colorVector;
-
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
-
- TPixel packed = default(TPixel);
- packed.PackFromVector4(finalColor);
- this.Target[targetX, targetY] = packed;
- }
+ amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
}
+
+ this.Blender.Compose(destinationRow, destinationRow, this.Colors, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs
index f29c37a67..b3ee2ed99 100644
--- a/src/ImageSharp.Drawing/FillRegion.cs
+++ b/src/ImageSharp.Drawing/FillRegion.cs
@@ -15,6 +15,20 @@ namespace ImageSharp
///
public static partial class ImageExtensions
{
+ ///
+ /// Flood fills the image with the specified brush.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The details how to fill the region of interest.
+ /// The graphics options.
+ /// The .
+ public static Image Fill(this Image source, IBrush brush, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return source.Apply(new FillProcessor(brush, options));
+ }
+
///
/// Flood fills the image with the specified brush.
///
@@ -25,7 +39,7 @@ namespace ImageSharp
public static Image Fill(this Image source, IBrush brush)
where TPixel : struct, IPixel
{
- return source.Apply(new FillProcessor(brush));
+ return source.Fill(brush, GraphicsOptions.Default);
}
///
diff --git a/src/ImageSharp.Drawing/GraphicsOptions.cs b/src/ImageSharp.Drawing/GraphicsOptions.cs
index a21617ead..2ceb654d3 100644
--- a/src/ImageSharp.Drawing/GraphicsOptions.cs
+++ b/src/ImageSharp.Drawing/GraphicsOptions.cs
@@ -5,6 +5,8 @@
namespace ImageSharp.Drawing
{
+ using ImageSharp.PixelFormats;
+
///
/// Options for influencing the drawing functions.
///
@@ -15,24 +17,60 @@ namespace ImageSharp.Drawing
///
public static readonly GraphicsOptions Default = new GraphicsOptions(true);
+ private float? blendPercentage;
+
+ private int? antialiasSubpixelDepth;
+
+ private bool? antialias;
+
+ private PixelBlenderMode blenderMode;
+
///
- /// Whether antialiasing should be applied.
+ /// Initializes a new instance of the struct.
///
- public bool Antialias;
+ /// If set to true [enable antialiasing].
+ public GraphicsOptions(bool enableAntialiasing)
+ {
+ this.blenderMode = PixelBlenderMode.Default;
+ this.blendPercentage = 1;
+ this.antialiasSubpixelDepth = 16;
+ this.antialias = enableAntialiasing;
+ }
///
- /// The number of subpixels to use while rendering with antialiasing enabled.
+ /// Gets or sets a value indicating whether antialiasing should be applied.
///
- public int AntialiasSubpixelDepth;
+ public bool Antialias
+ {
+ get => this.antialias ?? true;
+ set => this.antialias = value;
+ }
///
- /// Initializes a new instance of the struct.
+ /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
///
- /// If set to true [enable antialiasing].
- public GraphicsOptions(bool enableAntialiasing)
+ public int AntialiasSubpixelDepth
+ {
+ get => this.antialiasSubpixelDepth ?? 16;
+ set => this.antialiasSubpixelDepth = value;
+ }
+
+ ///
+ /// Gets or sets a value indicating the blending percentage to apply to the drawing operation
+ ///
+ public float BlendPercentage
+ {
+ get => this.blendPercentage ?? 1;
+ set => this.blendPercentage = value;
+ }
+
+ ///
+ /// Gets or sets a value indicating the blending percentage to apply to the drawing operation
+ ///
+ public PixelBlenderMode BlenderMode
{
- this.Antialias = enableAntialiasing;
- this.AntialiasSubpixelDepth = 16;
+ get => this.blenderMode;
+ set => this.blenderMode = value;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Pens/IPen.cs b/src/ImageSharp.Drawing/Pens/IPen.cs
index 31a609c8e..d488dbfb0 100644
--- a/src/ImageSharp.Drawing/Pens/IPen.cs
+++ b/src/ImageSharp.Drawing/Pens/IPen.cs
@@ -20,12 +20,13 @@ namespace ImageSharp.Drawing.Pens
///
/// The pixel source.
/// The region the pen will be applied to.
+ /// The currently active graphic options.
///
/// Returns a the applicator for the pen.
///
///
/// The when being applied to things like shapes would usually be the bounding box of the shape not necessarily the shape of the whole image.
///
- PenApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region);
+ PenApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region, GraphicsOptions options);
}
}
diff --git a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs
index f49d03cbc..1da50e0d6 100644
--- a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs
@@ -103,6 +103,7 @@ namespace ImageSharp.Drawing.Pens
///
/// The source pixels.
/// The region the pen will be applied to.
+ /// The Graphics options
///
/// Returns a the applicator for the pen.
///
@@ -110,16 +111,16 @@ namespace ImageSharp.Drawing.Pens
/// The when being applied to things like shapes would ussually be the
/// bounding box of the shape not necorserrally the shape of the whole image
///
- public PenApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region)
+ public PenApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options)
{
if (this.pattern == null || this.pattern.Length < 2)
{
// if there is only one item in the pattern then 100% of it will
// be solid so use the quicker applicator
- return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width);
+ return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width, options);
}
- return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern);
+ return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern, options);
}
private class SolidPenApplicator : PenApplicator
@@ -127,9 +128,9 @@ namespace ImageSharp.Drawing.Pens
private readonly BrushApplicator brush;
private readonly float halfWidth;
- public SolidPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width)
+ public SolidPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, GraphicsOptions options)
{
- this.brush = brush.CreateApplicator(sourcePixels, region);
+ this.brush = brush.CreateApplicator(sourcePixels, region, options);
this.halfWidth = width / 2;
this.RequiredRegion = RectangleF.Outset(region, width);
}
@@ -170,9 +171,9 @@ namespace ImageSharp.Drawing.Pens
private readonly float[] pattern;
private readonly float totalLength;
- public PatternPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, float[] pattern)
+ public PatternPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, float[] pattern, GraphicsOptions options)
{
- this.brush = brush.CreateApplicator(sourcePixels, region);
+ this.brush = brush.CreateApplicator(sourcePixels, region, options);
this.halfWidth = width / 2;
this.totalLength = 0;
diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
index 62e366d2a..124722cc0 100644
--- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
@@ -55,7 +55,7 @@ namespace ImageSharp.Drawing.Processors
protected override void OnApply(ImageBase source, Rectangle sourceRectangle)
{
using (PixelAccessor sourcePixels = source.Lock())
- using (PenApplicator applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds))
+ using (PenApplicator applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds, this.Options))
{
Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion);
diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs
index ca2dc9982..0634a06a3 100644
--- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs
@@ -24,14 +24,17 @@ namespace ImageSharp.Drawing.Processors
/// The brush.
///
private readonly IBrush brush;
+ private readonly GraphicsOptions options;
///
/// Initializes a new instance of the class.
///
/// The brush to source pixel colors from.
- public FillProcessor(IBrush brush)
+ /// The options
+ public FillProcessor(IBrush brush, GraphicsOptions options)
{
this.brush = brush;
+ this.options = options;
}
///
@@ -63,7 +66,7 @@ namespace ImageSharp.Drawing.Processors
// for example If brush is SolidBrush then we could just get the color upfront
// and skip using the IBrushApplicator?.
using (PixelAccessor sourcePixels = source.Lock())
- using (BrushApplicator applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle))
+ using (BrushApplicator applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle, this.options))
{
Parallel.For(
minY,
diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
index af1e6fa89..323214fb8 100644
--- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
@@ -88,104 +88,105 @@ namespace ImageSharp.Drawing.Processors
}
using (PixelAccessor sourcePixels = source.Lock())
- using (BrushApplicator applicator = this.Brush.CreateApplicator(sourcePixels, rect))
+ using (BrushApplicator applicator = this.Brush.CreateApplicator(sourcePixels, rect, this.Options))
{
float[] buffer = arrayPool.Rent(maxIntersections);
int scanlineWidth = maxX - minX;
- float[] scanline = ArrayPool.Shared.Rent(scanlineWidth);
- try
+ using (Buffer scanline = new Buffer(scanlineWidth))
{
- bool scanlineDirty = true;
- for (int y = minY; y < maxY; y++)
+ try
{
- if (scanlineDirty)
+ bool scanlineDirty = true;
+ for (int y = minY; y < maxY; y++)
{
- // clear the buffer
- for (int x = 0; x < scanlineWidth; x++)
+ if (scanlineDirty)
{
- scanline[x] = 0;
- }
-
- scanlineDirty = false;
- }
+ // clear the buffer
+ for (int x = 0; x < scanlineWidth; x++)
+ {
+ scanline[x] = 0;
+ }
- float subpixelFraction = 1f / subpixelCount;
- float subpixelFractionPoint = subpixelFraction / subpixelCount;
- for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
- {
- int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0);
- if (pointsFound == 0)
- {
- // nothing on this line skip
- continue;
+ scanlineDirty = false;
}
- QuickSort(buffer, pointsFound);
-
- for (int point = 0; point < pointsFound; point += 2)
+ float subpixelFraction = 1f / subpixelCount;
+ float subpixelFractionPoint = subpixelFraction / subpixelCount;
+ for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{
- // points will be paired up
- float scanStart = buffer[point] - minX;
- float scanEnd = buffer[point + 1] - minX;
- int startX = (int)MathF.Floor(scanStart);
- int endX = (int)MathF.Floor(scanEnd);
+ int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0);
+ if (pointsFound == 0)
+ {
+ // nothing on this line skip
+ continue;
+ }
- if (startX >= 0 && startX < scanline.Length)
+ QuickSort(buffer, pointsFound);
+
+ for (int point = 0; point < pointsFound; point += 2)
{
- for (float x = scanStart; x < startX + 1; x += subpixelFraction)
+ // points will be paired up
+ float scanStart = buffer[point] - minX;
+ float scanEnd = buffer[point + 1] - minX;
+ int startX = (int)MathF.Floor(scanStart);
+ int endX = (int)MathF.Floor(scanEnd);
+
+ if (startX >= 0 && startX < scanline.Length)
{
- scanline[startX] += subpixelFractionPoint;
- scanlineDirty = true;
+ for (float x = scanStart; x < startX + 1; x += subpixelFraction)
+ {
+ scanline[startX] += subpixelFractionPoint;
+ scanlineDirty = true;
+ }
}
- }
- if (endX >= 0 && endX < scanline.Length)
- {
- for (float x = endX; x < scanEnd; x += subpixelFraction)
+ if (endX >= 0 && endX < scanline.Length)
{
- scanline[endX] += subpixelFractionPoint;
- scanlineDirty = true;
+ for (float x = endX; x < scanEnd; x += subpixelFraction)
+ {
+ scanline[endX] += subpixelFractionPoint;
+ scanlineDirty = true;
+ }
}
- }
- int nextX = startX + 1;
- endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
- if (nextX >= 0)
- {
- for (int x = nextX; x < endX; x++)
+ int nextX = startX + 1;
+ endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
+ if (nextX >= 0)
{
- scanline[x] += subpixelFraction;
- scanlineDirty = true;
+ for (int x = nextX; x < endX; x++)
+ {
+ scanline[x] += subpixelFraction;
+ scanlineDirty = true;
+ }
}
}
}
- }
- if (scanlineDirty)
- {
- if (!this.Options.Antialias)
+ if (scanlineDirty)
{
- for (int x = 0; x < scanlineWidth; x++)
+ if (!this.Options.Antialias)
{
- if (scanline[x] > 0.5)
- {
- scanline[x] = 1;
- }
- else
+ for (int x = 0; x < scanlineWidth; x++)
{
- scanline[x] = 0;
+ if (scanline[x] > 0.5)
+ {
+ scanline[x] = 1;
+ }
+ else
+ {
+ scanline[x] = 0;
+ }
}
}
- }
- applicator.Apply(scanline, scanlineWidth, 0, minX, y);
+ applicator.Apply(scanline, minX, y);
+ }
}
}
- }
- finally
- {
- arrayPool.Return(buffer);
- ArrayPool.Shared.Return(scanline);
+ finally
+ {
+ arrayPool.Return(buffer);
+ }
}
}
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 05ba5ee60..904aa1ff6 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -623,13 +623,13 @@ namespace ImageSharp.Formats
case PngColorType.Rgb:
- BulkPixelOperations.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width);
+ PixelOperations.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width);
break;
case PngColorType.RgbWithAlpha:
- BulkPixelOperations.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width);
+ PixelOperations.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width);
break;
}
diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs
index 3c6763886..1e46a672e 100644
--- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs
+++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs
@@ -116,7 +116,7 @@ namespace ImageSharp
///
BufferSpan IBuffer2D.Span => this.pixelBuffer;
- private static BulkPixelOperations Operations => BulkPixelOperations.Instance;
+ private static PixelOperations Operations => PixelOperations.Instance;
///
/// Gets or sets the pixel at the specified position.
diff --git a/src/ImageSharp/PixelFormats/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs
index 5e2fff5d0..5a6eebf40 100644
--- a/src/ImageSharp/PixelFormats/Alpha8.cs
+++ b/src/ImageSharp/PixelFormats/Alpha8.cs
@@ -60,7 +60,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/Argb32.cs b/src/ImageSharp/PixelFormats/Argb32.cs
index a6db505bb..2798c4c5a 100644
--- a/src/ImageSharp/PixelFormats/Argb32.cs
+++ b/src/ImageSharp/PixelFormats/Argb32.cs
@@ -221,7 +221,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/Bgr565.cs b/src/ImageSharp/PixelFormats/Bgr565.cs
index a3d8cbcf2..78442a3ce 100644
--- a/src/ImageSharp/PixelFormats/Bgr565.cs
+++ b/src/ImageSharp/PixelFormats/Bgr565.cs
@@ -69,7 +69,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
/// Expands the packed representation into a .
diff --git a/src/ImageSharp/PixelFormats/Bgra4444.cs b/src/ImageSharp/PixelFormats/Bgra4444.cs
index 8d4e9b4f4..9138e25ca 100644
--- a/src/ImageSharp/PixelFormats/Bgra4444.cs
+++ b/src/ImageSharp/PixelFormats/Bgra4444.cs
@@ -68,7 +68,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/Bgra5551.cs b/src/ImageSharp/PixelFormats/Bgra5551.cs
index 8ae1cd430..9ff571243 100644
--- a/src/ImageSharp/PixelFormats/Bgra5551.cs
+++ b/src/ImageSharp/PixelFormats/Bgra5551.cs
@@ -69,7 +69,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/Byte4.cs b/src/ImageSharp/PixelFormats/Byte4.cs
index acb73dd1c..b27952ce4 100644
--- a/src/ImageSharp/PixelFormats/Byte4.cs
+++ b/src/ImageSharp/PixelFormats/Byte4.cs
@@ -71,7 +71,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/HalfSingle.cs b/src/ImageSharp/PixelFormats/HalfSingle.cs
index 893667fc7..2a686f535 100644
--- a/src/ImageSharp/PixelFormats/HalfSingle.cs
+++ b/src/ImageSharp/PixelFormats/HalfSingle.cs
@@ -73,7 +73,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
/// Expands the packed representation into a .
diff --git a/src/ImageSharp/PixelFormats/HalfVector2.cs b/src/ImageSharp/PixelFormats/HalfVector2.cs
index bd3cda55d..ef9676b56 100644
--- a/src/ImageSharp/PixelFormats/HalfVector2.cs
+++ b/src/ImageSharp/PixelFormats/HalfVector2.cs
@@ -83,7 +83,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
/// Expands the packed representation into a .
diff --git a/src/ImageSharp/PixelFormats/HalfVector4.cs b/src/ImageSharp/PixelFormats/HalfVector4.cs
index 03e4326b7..8b284509f 100644
--- a/src/ImageSharp/PixelFormats/HalfVector4.cs
+++ b/src/ImageSharp/PixelFormats/HalfVector4.cs
@@ -86,7 +86,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs
index 43fe81e95..9a8d9730a 100644
--- a/src/ImageSharp/PixelFormats/IPixel.cs
+++ b/src/ImageSharp/PixelFormats/IPixel.cs
@@ -16,11 +16,11 @@ namespace ImageSharp.PixelFormats
where TSelf : struct, IPixel
{
///
- /// Creates a instance for this pixel type.
- /// This method is not intended to be consumed directly. Use instead.
+ /// Creates a instance for this pixel type.
+ /// This method is not intended to be consumed directly. Use instead.
///
- /// The instance.
- BulkPixelOperations CreateBulkOperations();
+ /// The instance.
+ PixelOperations CreateBulkOperations();
}
///
diff --git a/src/ImageSharp/PixelFormats/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/NormalizedByte2.cs
index dab113ae7..c5b6eff6b 100644
--- a/src/ImageSharp/PixelFormats/NormalizedByte2.cs
+++ b/src/ImageSharp/PixelFormats/NormalizedByte2.cs
@@ -89,7 +89,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
/// Expands the packed representation into a .
diff --git a/src/ImageSharp/PixelFormats/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/NormalizedByte4.cs
index 0cb5c756b..9e3676660 100644
--- a/src/ImageSharp/PixelFormats/NormalizedByte4.cs
+++ b/src/ImageSharp/PixelFormats/NormalizedByte4.cs
@@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/NormalizedShort2.cs
index 86d80cbad..01a2d9954 100644
--- a/src/ImageSharp/PixelFormats/NormalizedShort2.cs
+++ b/src/ImageSharp/PixelFormats/NormalizedShort2.cs
@@ -88,7 +88,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/NormalizedShort4.cs
index 8512d4131..3e4535fdf 100644
--- a/src/ImageSharp/PixelFormats/NormalizedShort4.cs
+++ b/src/ImageSharp/PixelFormats/NormalizedShort4.cs
@@ -90,7 +90,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs
new file mode 100644
index 000000000..c189fefdc
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs
@@ -0,0 +1,83 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ ///
+ /// The various blending modes.
+ ///
+ public enum PixelBlenderMode
+ {
+ ///
+ /// The default composition mode.
+ ///
+ /// uses PremultipliedLerpTransform
+ Default = 0,
+
+ ///
+ /// Normal transform.
+ ///
+ Normal,
+
+ ///
+ /// Multiply Transform.
+ ///
+ Multiply,
+
+ ///
+ /// Screen Transform.
+ ///
+ Screen,
+
+ ///
+ /// HardLight Transform.
+ ///
+ HardLight,
+
+ ///
+ /// Overlay Transform.
+ ///
+ Overlay,
+
+ ///
+ /// Darken Transform.
+ ///
+ Darken,
+
+ ///
+ /// Lighten Transform.
+ ///
+ Lighten,
+
+ ///
+ /// SoftLight Transform.
+ ///
+ SoftLight,
+
+ ///
+ /// Dodge Transform.
+ ///
+ Dodge,
+
+ ///
+ /// Burn Transform.
+ ///
+ Burn,
+
+ ///
+ /// Difference Transform.
+ ///
+ Difference,
+
+ ///
+ /// Exclusion Transform.
+ ///
+ Exclusion
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..f7a71e6a1
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultBurnPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Burn(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Burn(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..521ae7bf1
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultDarkenPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Darken(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Darken(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs
new file mode 100644
index 000000000..08e1d36dd
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultDifferencePixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Difference(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Difference(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs
new file mode 100644
index 000000000..cd2c4e4b8
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultDodgePixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Dodge(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Dodge(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..817fe82c7
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultExclusionPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Exclusion(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Exclusion(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..41c1005f6
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultHardLightPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.HardLight(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.HardLight(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..6de12372b
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultLightenPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Lighten(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Lighten(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..8240c2b87
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultMultiplyPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Multiply(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Multiply(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..cc58498b3
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs
@@ -0,0 +1,37 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultNormalPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ return source;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ destination[i] = source[i];
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..042543608
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultOverlayPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Overlay(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Overlay(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..32ab08796
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultPremultipliedLerpPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.PremultipliedLerp(background.ToVector4(), source.ToVector4(), amount);
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.PremultipliedLerp(background[i].ToVector4(), source[i].ToVector4(), amount[i]);
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..be578af81
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultScreenPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.Screen(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.Screen(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..357e084c9
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs
@@ -0,0 +1,42 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats.PixelBlenders
+{
+ using System;
+ using System.Numerics;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal class DefaultSoftLightPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ public override TPixel Compose(TPixel background, TPixel source, float amount)
+ {
+ Vector4 result = Vector4BlendTransforms.SoftLight(background.ToVector4(), source.ToVector4());
+ TPixel resultPixel = default(TPixel);
+ resultPixel.PackFromVector4(result);
+ return resultPixel;
+ }
+
+ ///
+ public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Vector4 result = Vector4BlendTransforms.SoftLight(background[i].ToVector4(), source[i].ToVector4());
+ destination[i].PackFromVector4(result);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
new file mode 100644
index 000000000..ee0c67396
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
@@ -0,0 +1,39 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats
+{
+ ///
+ /// Abstract base class for calling pixel composition functions
+ ///
+ /// The type of the pixel
+ internal abstract class PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Composes 2 pixels together.
+ ///
+ /// The background color.
+ /// The source color.
+ ///
+ /// A value between 0 and 1 indicating the weight of the second source vector.
+ /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
+ ///
+ /// The final pixel value after composition
+ public abstract TPixel Compose(TPixel background, TPixel source, float amount);
+
+ ///
+ /// Composes 2 pixels together.
+ ///
+ /// The destination span.
+ /// The background span.
+ /// The source span.
+ ///
+ /// A value between 0 and 1 indicating the weight of the second source vector.
+ /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
+ ///
+ public abstract void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount);
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
new file mode 100644
index 000000000..5ab3449f2
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
@@ -0,0 +1,125 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.PixelFormats
+{
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+ using ImageSharp.PixelFormats.PixelBlenders;
+
+#pragma warning disable CS1710 // XML comment has a duplicate typeparam tag
+ ///
+ /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations
+ /// for pixel buffers of type .
+ ///
+ /// The pixel format.
+ public partial class PixelOperations
+#pragma warning restore CS1710 // XML comment has a duplicate typeparam tag
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Gets the NormalBlender.
+ ///
+ private PixelBlender normalBlender = new DefaultNormalPixelBlender();
+
+ ///
+ /// Gets the MultiplyBlender.
+ ///
+ private PixelBlender multiplyBlender = new DefaultMultiplyPixelBlender();
+
+ ///
+ /// Gets the ScreenBlender.
+ ///
+ private PixelBlender screenBlender = new DefaultScreenPixelBlender();
+
+ ///
+ /// Gets the HardLightBlender.
+ ///
+ private PixelBlender hardLightBlender = new DefaultHardLightPixelBlender();
+
+ ///
+ /// Gets the OverlayBlender.
+ ///
+ private PixelBlender overlayBlender = new DefaultOverlayPixelBlender();
+
+ ///
+ /// Gets the DarkenBlender.
+ ///
+ private PixelBlender darkenBlender = new DefaultDarkenPixelBlender();
+
+ ///
+ /// Gets the LightenBlender.
+ ///
+ private PixelBlender lightenBlender = new DefaultLightenPixelBlender();
+
+ ///
+ /// Gets the SoftLightBlender.
+ ///
+ private PixelBlender softLightBlender = new DefaultSoftLightPixelBlender();
+
+ ///
+ /// Gets the DodgeBlender.
+ ///
+ private PixelBlender dodgeBlender = new DefaultDodgePixelBlender();
+
+ ///
+ /// Gets the BurnBlender.
+ ///
+ private PixelBlender burnBlender = new DefaultBurnPixelBlender();
+
+ ///
+ /// Gets the DifferenceBlender.
+ ///
+ private PixelBlender differenceBlender = new DefaultDifferencePixelBlender();
+
+ ///
+ /// Gets the DifferenceBlender.
+ ///
+ private PixelBlender exclusionBlender = new DefaultExclusionPixelBlender();
+
+ ///
+ /// Gets the PremultipliedLerpBlender.
+ ///
+ private PixelBlender premultipliedLerpBlender = new DefaultPremultipliedLerpPixelBlender();
+
+ ///
+ /// Find an instance of the pixel blender.
+ ///
+ /// The blending mode to apply
+ /// A .
+ internal virtual PixelBlender GetPixelBlender(PixelBlenderMode mode)
+ {
+ switch (mode)
+ {
+ case PixelBlenderMode.Normal:
+ return this.normalBlender;
+ case PixelBlenderMode.Multiply:
+ return this.multiplyBlender;
+ case PixelBlenderMode.Screen:
+ return this.screenBlender;
+ case PixelBlenderMode.HardLight:
+ return this.hardLightBlender;
+ case PixelBlenderMode.Overlay:
+ return this.overlayBlender;
+ case PixelBlenderMode.Darken:
+ return this.darkenBlender;
+ case PixelBlenderMode.Lighten:
+ return this.lightenBlender;
+ case PixelBlenderMode.SoftLight:
+ return this.softLightBlender;
+ case PixelBlenderMode.Dodge:
+ return this.dodgeBlender;
+ case PixelBlenderMode.Burn:
+ return this.burnBlender;
+ case PixelBlenderMode.Difference:
+ return this.differenceBlender;
+ case PixelBlenderMode.Exclusion:
+ return this.exclusionBlender;
+ default:
+ return this.premultipliedLerpBlender;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/BulkPixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
similarity index 91%
rename from src/ImageSharp/PixelFormats/BulkPixelOperations{TPixel}.cs
rename to src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
index 9a3d266c1..11ff42222 100644
--- a/src/ImageSharp/PixelFormats/BulkPixelOperations{TPixel}.cs
+++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
@@ -8,18 +8,20 @@ namespace ImageSharp.PixelFormats
using System.Numerics;
using System.Runtime.CompilerServices;
- ///
- /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations
- /// for pixel buffers of type .
- ///
- /// The pixel format.
- public class BulkPixelOperations
+#pragma warning disable CS1710 // XML comment has a duplicate typeparam tag
+ ///
+ /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations
+ /// for pixel buffers of type .
+ ///
+ /// The pixel format.
+ public partial class PixelOperations
+#pragma warning restore CS1710 // XML comment has a duplicate typeparam tag
where TPixel : struct, IPixel
{
///
- /// Gets the global instance for the pixel type
+ /// Gets the global instance for the pixel type
///
- public static BulkPixelOperations Instance { get; } = default(TPixel).CreateBulkOperations();
+ public static PixelOperations Instance { get; } = default(TPixel).CreateBulkOperations();
///
/// Bulk version of
diff --git a/src/ImageSharp/PixelFormats/Rg32.cs b/src/ImageSharp/PixelFormats/Rg32.cs
index a4bfc5823..4d1f0fecf 100644
--- a/src/ImageSharp/PixelFormats/Rg32.cs
+++ b/src/ImageSharp/PixelFormats/Rg32.cs
@@ -74,7 +74,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
/// Expands the packed representation into a .
diff --git a/src/ImageSharp/PixelFormats/Rgba1010102.cs b/src/ImageSharp/PixelFormats/Rgba1010102.cs
index cfd60f410..96f7af773 100644
--- a/src/ImageSharp/PixelFormats/Rgba1010102.cs
+++ b/src/ImageSharp/PixelFormats/Rgba1010102.cs
@@ -77,7 +77,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/Rgba32.BulkOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs
similarity index 97%
rename from src/ImageSharp/PixelFormats/Rgba32.BulkOperations.cs
rename to src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs
index df21cdc70..ff284e625 100644
--- a/src/ImageSharp/PixelFormats/Rgba32.BulkOperations.cs
+++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
@@ -16,9 +16,9 @@ namespace ImageSharp.PixelFormats
public partial struct Rgba32
{
///
- /// implementation optimized for .
+ /// implementation optimized for .
///
- internal class BulkOperations : BulkPixelOperations
+ internal class PixelOperations : PixelOperations
{
///
/// SIMD optimized bulk implementation of
diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/Rgba32.cs
index d668be76f..0d5926910 100644
--- a/src/ImageSharp/PixelFormats/Rgba32.cs
+++ b/src/ImageSharp/PixelFormats/Rgba32.cs
@@ -202,7 +202,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/Rgba64.cs b/src/ImageSharp/PixelFormats/Rgba64.cs
index 20bba9f41..b7ff143a9 100644
--- a/src/ImageSharp/PixelFormats/Rgba64.cs
+++ b/src/ImageSharp/PixelFormats/Rgba64.cs
@@ -76,7 +76,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations CreateBulkOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/RgbaVector.BulkOperations.cs b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs
similarity index 81%
rename from src/ImageSharp/PixelFormats/RgbaVector.BulkOperations.cs
rename to src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs
index 9dcfc9074..da0900c11 100644
--- a/src/ImageSharp/PixelFormats/RgbaVector.BulkOperations.cs
+++ b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs
@@ -13,9 +13,9 @@ namespace ImageSharp.PixelFormats
public partial struct RgbaVector
{
///
- /// implementation optimized for .
+ /// implementation optimized for .
///
- internal class BulkOperations : BulkPixelOperations
+ internal class PixelOperations : PixelOperations
{
///
internal override unsafe void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count)
diff --git a/src/ImageSharp/PixelFormats/RgbaVector.cs b/src/ImageSharp/PixelFormats/RgbaVector.cs
index c59e93259..25eaa9711 100644
--- a/src/ImageSharp/PixelFormats/RgbaVector.cs
+++ b/src/ImageSharp/PixelFormats/RgbaVector.cs
@@ -208,7 +208,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new RgbaVector.BulkOperations();
+ public PixelOperations CreateBulkOperations() => new RgbaVector.PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/PixelFormats/Short2.cs b/src/ImageSharp/PixelFormats/Short2.cs
index 60fbb5b35..d681e27b7 100644
--- a/src/ImageSharp/PixelFormats/Short2.cs
+++ b/src/ImageSharp/PixelFormats/Short2.cs
@@ -89,7 +89,7 @@ namespace ImageSharp.PixelFormats
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public PixelOperations