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..5038ea01b 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.Blend(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
index ad37f4d28..dc8a4bc90 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).Clamp(0, 1);
- // 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[patternY, patternX];
}
+
+ BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
+ this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
index 5dd6dad76..d7c70220c 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.Blend(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
index 7c192b2d3..a96202b7b 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);
}
///
@@ -72,25 +72,29 @@ namespace ImageSharp.Drawing.Brushes
///
/// The target color.
///
- private readonly Vector4 targeTPixel;
+ private readonly Vector4 targetColor;
///
/// The threshold.
///
private readonly float threshold;
+ private readonly TPixel targetColorPixel;
+
///
/// Initializes a new instance of the class.
///
/// The source pixels.
/// Color of the source.
- /// Color of the target.
+ /// 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 targetColor, float threshold, GraphicsOptions options)
+ : base(sourcePixels, options)
{
this.sourceColor = sourceColor.ToVector4();
- this.targeTPixel = targeTPixel.ToVector4();
+ this.targetColor = targetColor.ToVector4();
+ this.targetColorPixel = targetColor;
// Lets hack a min max extreams for a color space by letteing the IPackedPixel clamp our values to something in the correct spaces :)
TPixel maxColor = default(TPixel);
@@ -119,11 +123,10 @@ namespace ImageSharp.Drawing.Brushes
if (distance <= this.threshold)
{
float lerpAmount = (this.threshold - distance) / this.threshold;
- Vector4 blended = Vector4BlendTransforms.PremultipliedLerp(
- background,
- this.targeTPixel,
+ return this.Blender.Blend(
+ result,
+ this.targetColorPixel,
lerpAmount);
- result.PackFromVector4(blended);
}
return result;
@@ -136,42 +139,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.Blend(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
index 634a2b70d..71b802136 100644
--- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
@@ -39,9 +39,9 @@ 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);
+ return new SolidBrushApplicator(sourcePixels, this.color, options);
}
///
@@ -49,24 +49,27 @@ namespace ImageSharp.Drawing.Brushes
///
private class SolidBrushApplicator : BrushApplicator
{
- ///
- /// The solid color.
- ///
- private readonly TPixel color;
- private readonly Vector4 colorVector;
-
///
/// 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 +78,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.Blend(destinationRow, destinationRow, this.Colors, amountBuffer);
}
}
}
diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs
index 6a4f49337..acc821292 100644
--- a/src/ImageSharp.Drawing/DrawImage.cs
+++ b/src/ImageSharp.Drawing/DrawImage.cs
@@ -1,57 +1,131 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp
-{
- using Drawing.Processors;
- using ImageSharp.PixelFormats;
-
- ///
- /// Extension methods for the type.
- ///
- public static partial class ImageExtensions
- {
- ///
- /// Draws the given image together with the current one by blending their pixels.
- ///
- /// The pixel format.
- /// The image this method extends.
- /// The image to blend with the currently processing image.
- /// The opacity of the image image to blend. Must be between 0 and 100.
- /// The .
- public static Image Blend(this Image source, Image image, int percent = 50)
- where TPixel : struct, IPixel
- {
- return DrawImage(source, image, percent, default(Size), default(Point));
- }
-
- ///
- /// Draws the given image together with the current one by blending their pixels.
- ///
- /// The image this method extends.
- /// The image to blend with the currently processing image.
- /// The pixel format.
- /// The opacity of the image image to blend. Must be between 0 and 100.
- /// The size to draw the blended image.
- /// The location to draw the blended image.
- /// The .
- public static Image DrawImage(this Image source, Image image, int percent, Size size, Point location)
- where TPixel : struct, IPixel
- {
- if (size == default(Size))
- {
- size = new Size(image.Width, image.Height);
- }
-
- if (location == default(Point))
- {
- location = Point.Empty;
- }
-
- source.ApplyProcessor(new DrawImageProcessor(image, size, location, percent), source.Bounds);
- return source;
- }
- }
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using Drawing.Processors;
+ using ImageSharp.Drawing;
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Draws the given image together with the current one by blending their pixels.
+ ///
+ /// The image this method extends.
+ /// The image to blend with the currently processing image.
+ /// The pixel format.
+ /// The size to draw the blended image.
+ /// The location to draw the blended image.
+ /// The options.
+ /// The .
+ public static Image DrawImage(this Image source, Image image, Size size, Point location, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ if (size == default(Size))
+ {
+ size = new Size(image.Width, image.Height);
+ }
+
+ if (location == default(Point))
+ {
+ location = Point.Empty;
+ }
+
+ source.ApplyProcessor(new DrawImageProcessor(image, size, location, options), source.Bounds);
+ return source;
+ }
+
+ ///
+ /// Draws the given image together with the current one by blending their pixels.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The image to blend with the currently processing image.
+ /// The opacity of the image image to blend. Must be between 0 and 1.
+ /// The .
+ public static Image Blend(this Image source, Image image, float percent)
+ where TPixel : struct, IPixel
+ {
+ GraphicsOptions options = GraphicsOptions.Default;
+ options.BlendPercentage = percent;
+ return DrawImage(source, image, default(Size), default(Point), options);
+ }
+
+ ///
+ /// Draws the given image together with the current one by blending their pixels.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The image to blend with the currently processing image.
+ /// The blending mode.
+ /// The opacity of the image image to blend. Must be between 0 and 1.
+ /// The .
+ public static Image Blend(this Image source, Image image, PixelBlenderMode blender, float percent)
+ where TPixel : struct, IPixel
+ {
+ GraphicsOptions options = GraphicsOptions.Default;
+ options.BlendPercentage = percent;
+ options.BlenderMode = blender;
+ return DrawImage(source, image, default(Size), default(Point), options);
+ }
+
+ ///
+ /// Draws the given image together with the current one by blending their pixels.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The image to blend with the currently processing image.
+ /// The options, including the blending type and belnding amount.
+ /// The .
+ public static Image Blend(this Image source, Image image, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return DrawImage(source, image, default(Size), default(Point), options);
+ }
+
+ ///
+ /// Draws the given image together with the current one by blending their pixels.
+ ///
+ /// The image this method extends.
+ /// The image to blend with the currently processing image.
+ /// The pixel format.
+ /// The opacity of the image image to blend. Must be between 0 and 1.
+ /// The size to draw the blended image.
+ /// The location to draw the blended image.
+ /// The .
+ public static Image DrawImage(this Image source, Image image, float percent, Size size, Point location)
+ where TPixel : struct, IPixel
+ {
+ GraphicsOptions options = GraphicsOptions.Default;
+ options.BlendPercentage = percent;
+ return source.DrawImage(image, size, location, options);
+ }
+
+ ///
+ /// Draws the given image together with the current one by blending their pixels.
+ ///
+ /// The image this method extends.
+ /// The image to blend with the currently processing image.
+ /// The pixel format.
+ /// The type of bending to apply.
+ /// The opacity of the image image to blend. Must be between 0 and 1.
+ /// The size to draw the blended image.
+ /// The location to draw the blended image.
+ /// The .
+ public static Image DrawImage(this Image source, Image image, PixelBlenderMode blender, float percent, Size size, Point location)
+ where TPixel : struct, IPixel
+ {
+ GraphicsOptions options = GraphicsOptions.Default;
+ options.BlenderMode = blender;
+ options.BlendPercentage = percent;
+ return source.DrawImage(image, size, location, options);
+ }
+ }
}
\ No newline at end of file
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
deleted file mode 100644
index a21617ead..000000000
--- a/src/ImageSharp.Drawing/GraphicsOptions.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Drawing
-{
- ///
- /// Options for influencing the drawing functions.
- ///
- public struct GraphicsOptions
- {
- ///
- /// Represents the default .
- ///
- public static readonly GraphicsOptions Default = new GraphicsOptions(true);
-
- ///
- /// Whether antialiasing should be applied.
- ///
- public bool Antialias;
-
- ///
- /// The number of subpixels to use while rendering with antialiasing enabled.
- ///
- public int AntialiasSubpixelDepth;
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// If set to true [enable antialiasing].
- public GraphicsOptions(bool enableAntialiasing)
- {
- this.Antialias = enableAntialiasing;
- this.AntialiasSubpixelDepth = 16;
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
index 7c483712d..9fb0e1e8d 100644
--- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
+++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
@@ -2,7 +2,7 @@
An extension to ImageSharp that allows the drawing of images, paths, and text.
ImageSharp.Drawing
- 1.0.0-alpha7
+ 1.0.0-alpha8
James Jackson-South and contributors
netstandard1.1
true
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/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
index e2a9ef024..c49631de8 100644
--- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
@@ -18,19 +18,22 @@ namespace ImageSharp.Drawing.Processors
internal class DrawImageProcessor : ImageProcessor
where TPixel : struct, IPixel
{
+ private readonly PixelBlender blender;
+
///
/// Initializes a new instance of the class.
///
/// The image to blend with the currently processing image.
/// The size to draw the blended image.
/// The location to draw the blended image.
- /// The opacity of the image to blend. Between 0 and 100.
- public DrawImageProcessor(Image image, Size size, Point location, int alpha = 100)
+ /// The opacity of the image to blend. Between 0 and 100.
+ public DrawImageProcessor(Image image, Size size, Point location, GraphicsOptions options)
{
- Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha));
+ Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage));
this.Image = image;
this.Size = size;
- this.Alpha = alpha;
+ this.Alpha = options.BlendPercentage;
+ this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode);
this.Location = location;
}
@@ -42,7 +45,7 @@ namespace ImageSharp.Drawing.Processors
///
/// Gets the alpha percentage value.
///
- public int Alpha { get; }
+ public float Alpha { get; }
///
/// Gets the size to draw the blended image.
@@ -57,43 +60,52 @@ namespace ImageSharp.Drawing.Processors
///
protected override void OnApply(ImageBase source, Rectangle sourceRectangle)
{
- if (this.Image.Bounds.Size != this.Size)
+ Image disposableImage = null;
+ Image targetImage = this.Image;
+
+ try
{
- // should Resize be moved to core?
- this.Image = this.Image.Resize(this.Size.Width, this.Size.Height);
- }
+ if (targetImage.Bounds.Size != this.Size)
+ {
+ targetImage = disposableImage = new Image(this.Image).Resize(this.Size.Width, this.Size.Height);
+ }
- // Align start/end positions.
- Rectangle bounds = this.Image.Bounds;
- int minX = Math.Max(this.Location.X, sourceRectangle.X);
- int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
- int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
- int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
+ // Align start/end positions.
+ Rectangle bounds = this.Image.Bounds;
+ int minX = Math.Max(this.Location.X, sourceRectangle.X);
+ int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
+ maxX = Math.Min(this.Location.X + this.Size.Width, maxX);
- float alpha = this.Alpha / 100F;
+ int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
+ int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
- using (PixelAccessor toBlendPixels = this.Image.Lock())
- using (PixelAccessor sourcePixels = source.Lock())
- {
- Parallel.For(
- minY,
- maxY,
- this.ParallelOptions,
- y =>
- {
- for (int x = minX; x < maxX; x++)
- {
- Vector4 backgroundVector = sourcePixels[x, y].ToVector4();
- Vector4 sourceVector = toBlendPixels[x - minX, y - minY].ToVector4();
+ maxY = Math.Min(this.Location.Y + this.Size.Height, maxY);
- // Lerping colors is dependent on the alpha of the blended color
- backgroundVector = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, alpha);
+ int width = maxX - minX;
+ using (Buffer amount = new Buffer(width))
+ using (PixelAccessor toBlendPixels = targetImage.Lock())
+ using (PixelAccessor sourcePixels = source.Lock())
+ {
+ for (int i = 0; i < width; i++)
+ {
+ amount[i] = this.Alpha;
+ }
- TPixel packed = default(TPixel);
- packed.PackFromVector4(backgroundVector);
- sourcePixels[x, y] = packed;
- }
- });
+ Parallel.For(
+ minY,
+ maxY,
+ this.ParallelOptions,
+ y =>
+ {
+ BufferSpan background = sourcePixels.GetRowSpan(y).Slice(minX, width);
+ BufferSpan foreground = toBlendPixels.GetRowSpan(y - this.Location.Y).Slice(0, width);
+ this.blender.Blend(background, background, foreground, amount);
+ });
+ }
+ }
+ finally
+ {
+ disposableImage?.Dispose();
}
}
}
diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
index 62e366d2a..3fd829549 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);
@@ -86,37 +86,34 @@ namespace ImageSharp.Drawing.Processors
polyStartY = 0;
}
+ int width = maxX - minX;
+ PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.Options.BlenderMode);
+
Parallel.For(
- minY,
- maxY,
- this.ParallelOptions,
- y =>
- {
- int offsetY = y - polyStartY;
+ minY,
+ maxY,
+ this.ParallelOptions,
+ y =>
+ {
+ int offsetY = y - polyStartY;
- for (int x = minX; x < maxX; x++)
+ using (Buffer amount = new Buffer(width))
+ using (Buffer colors = new Buffer(width))
+ {
+ for (int i = 0; i < width; i++)
{
- // TODO add find intersections code to skip and scan large regions of this.
+ int x = i + minX;
int offsetX = x - startX;
PointInfo info = this.Path.GetPointInfo(offsetX, offsetY);
-
ColoredPointInfo color = applicator.GetColor(offsetX, offsetY, info);
-
- float opacity = this.Opacity(color.DistanceFromElement);
-
- if (opacity > Constants.Epsilon)
- {
- Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4();
- Vector4 sourceVector = color.Color.ToVector4();
-
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
-
- TPixel packed = default(TPixel);
- packed.PackFromVector4(finalColor);
- sourcePixels[offsetX, offsetY] = packed;
- }
+ amount[i] = (this.Opacity(color.DistanceFromElement) * this.Options.BlendPercentage).Clamp(0, 1);
+ colors[i] = color.Color;
}
- });
+
+ BufferSpan destination = sourcePixels.GetRowSpan(offsetY).Slice(minX - startX, width);
+ blender.Blend(destination, destination, colors, amount);
+ }
+ });
}
}
diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs
index ca2dc9982..25eccd245 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;
}
///
@@ -59,32 +62,30 @@ namespace ImageSharp.Drawing.Processors
startY = 0;
}
+ int width = maxX - minX;
+
// we could possibly do some optermising by having knowledge about the individual brushes operate
// 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 (Buffer amount = new Buffer(width))
+ using (BrushApplicator applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle, this.options))
{
- Parallel.For(
+ for (int i = 0; i < width; i++)
+ {
+ amount[i] = this.options.BlendPercentage;
+ }
+
+ Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int offsetY = y - startY;
- for (int x = minX; x < maxX; x++)
- {
- int offsetX = x - startX;
-
- Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4();
- Vector4 sourceVector = applicator[offsetX, offsetY].ToVector4();
-
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, 1);
+ int offsetX = minX - startX;
- TPixel packed = default(TPixel);
- packed.PackFromVector4(finalColor);
- sourcePixels[offsetX, offsetY] = packed;
- }
+ applicator.Apply(amount, offsetX, offsetY);
});
}
}
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/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs
new file mode 100644
index 000000000..f45582e1e
--- /dev/null
+++ b/src/ImageSharp/GraphicsOptions.cs
@@ -0,0 +1,80 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using ImageSharp.PixelFormats;
+
+ ///
+ /// Options for influencing the drawing functions.
+ ///
+ public struct GraphicsOptions
+ {
+ ///
+ /// Represents the default .
+ ///
+ public static readonly GraphicsOptions Default = new GraphicsOptions(true);
+
+ private float? blendPercentage;
+
+ private int? antialiasSubpixelDepth;
+
+ private bool? antialias;
+
+ private PixelBlenderMode blenderMode;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// If set to true [enable antialiasing].
+ public GraphicsOptions(bool enableAntialiasing)
+ {
+ this.blenderMode = PixelBlenderMode.Normal;
+ this.blendPercentage = 1;
+ this.antialiasSubpixelDepth = 16;
+ this.antialias = enableAntialiasing;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether antialiasing should be applied.
+ ///
+ public bool Antialias
+ {
+ get => this.antialias ?? true;
+ set => this.antialias = value;
+ }
+
+ ///
+ /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
+ ///
+ 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).Clamp(0, 1);
+ set => this.blendPercentage = value;
+ }
+
+ // In the future we could expose a PixelBlender directly on here
+ // or some forms of PixelBlender factory for each pixel type. Will need
+ // some API thought post V1.
+
+ ///
+ /// Gets or sets a value indicating the blending percentage to apply to the drawing operation
+ ///
+ public PixelBlenderMode BlenderMode
+ {
+ get => this.blenderMode;
+ set => this.blenderMode = value;
+ }
+ }
+}
\ No newline at end of file
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/ImageProcessor.cs b/src/ImageSharp/ImageProcessor.cs
index 7b9f74547..745b25fb6 100644
--- a/src/ImageSharp/ImageProcessor.cs
+++ b/src/ImageSharp/ImageProcessor.cs
@@ -41,7 +41,11 @@ namespace ImageSharp.Processing
}
catch (Exception ex)
{
+#if DEBUG
+ throw;
+#else
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
+#endif
}
}
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index 64183d05c..16fff3212 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -2,7 +2,7 @@
A cross-platform library for the processing of image files; written in C#
ImageSharp
- 1.0.0-alpha7
+ 1.0.0-alpha8
James Jackson-South and contributors
netstandard1.3;netstandard1.1
true
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..d8031fe6e
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs
@@ -0,0 +1,62 @@
+//
+// 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
+ {
+ ///
+ /// Default blending mode, also known as "Normal" or "Alpha Blending"
+ ///
+ Normal = 0,
+
+ ///
+ /// Blends the 2 values by multiplication.
+ ///
+ Multiply,
+
+ ///
+ /// Blends the 2 values by addition.
+ ///
+ Add,
+
+ ///
+ /// Blends the 2 values by subtraction.
+ ///
+ Substract,
+
+ ///
+ /// Multiplies the complements of the backdrop and source values, then complements the result.
+ ///
+ Screen,
+
+ ///
+ /// Selects the minimum of the backdrop and source values.
+ ///
+ Darken,
+
+ ///
+ /// Selects the max of the backdrop and source values.
+ ///
+ Lighten,
+
+ ///
+ /// Multiplies or screens the values, depending on the backdrop vector values.
+ ///
+ Overlay,
+
+ ///
+ /// Multiplies or screens the colors, depending on the source value.
+ ///
+ HardLight
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..ab3aee041
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs
@@ -0,0 +1,55 @@
+//
+// 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;
+
+ ///
+ /// Applies an "Add" blending to pixels.
+ ///
+ /// The type of the pixel
+ internal class DefaultAddPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Gets the static instance of this blender.
+ ///
+ public static DefaultAddPixelBlender Instance { get; } = new DefaultAddPixelBlender();
+
+ ///
+ public override TPixel Blend(TPixel background, TPixel source, float amount)
+ {
+ return PorterDuffFunctions.AddFunction(background, source, amount);
+ }
+
+ ///
+ public override void Blend(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
+
+ using (Buffer buffer = new Buffer(destination.Length * 3))
+ {
+ BufferSpan destinationSpan = buffer.Slice(0, destination.Length);
+ BufferSpan backgroundSpan = buffer.Slice(destination.Length, destination.Length);
+ BufferSpan sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
+
+ PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length);
+ PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length);
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ destinationSpan[i] = PorterDuffFunctions.AddFunction(backgroundSpan[i], sourceSpan[i], amount[i]);
+ }
+
+ PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..e0ff80b66
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs
@@ -0,0 +1,55 @@
+//
+// 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;
+
+ ///
+ /// Applies an "Darken" blending to pixels.
+ ///
+ /// The type of the pixel
+ internal class DefaultDarkenPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Gets the static instance of this blender.
+ ///
+ public static DefaultDarkenPixelBlender Instance { get; } = new DefaultDarkenPixelBlender();
+
+ ///
+ public override TPixel Blend(TPixel background, TPixel source, float amount)
+ {
+ return PorterDuffFunctions.DarkenFunction(background, source, amount);
+ }
+
+ ///
+ public override void Blend(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
+
+ using (Buffer buffer = new Buffer(destination.Length * 3))
+ {
+ BufferSpan destinationSpan = buffer.Slice(0, destination.Length);
+ BufferSpan backgroundSpan = buffer.Slice(destination.Length, destination.Length);
+ BufferSpan sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
+
+ PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length);
+ PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length);
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ destinationSpan[i] = PorterDuffFunctions.DarkenFunction(backgroundSpan[i], sourceSpan[i], amount[i]);
+ }
+
+ PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..cec0dc0db
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs
@@ -0,0 +1,55 @@
+//
+// 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;
+
+ ///
+ /// Applies an "Hard Light" blending to pixels.
+ ///
+ /// The type of the pixel
+ internal class DefaultHardLightPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Gets the static instance of this blender.
+ ///
+ public static DefaultHardLightPixelBlender Instance { get; } = new DefaultHardLightPixelBlender();
+
+ ///
+ public override TPixel Blend(TPixel background, TPixel source, float amount)
+ {
+ return PorterDuffFunctions.HardLightFunction(background, source, amount);
+ }
+
+ ///
+ public override void Blend(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
+
+ using (Buffer buffer = new Buffer(destination.Length * 3))
+ {
+ BufferSpan destinationSpan = buffer.Slice(0, destination.Length);
+ BufferSpan backgroundSpan = buffer.Slice(destination.Length, destination.Length);
+ BufferSpan sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
+
+ PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length);
+ PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length);
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ destinationSpan[i] = PorterDuffFunctions.HardLightFunction(backgroundSpan[i], sourceSpan[i], amount[i]);
+ }
+
+ PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..32cd20650
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs
@@ -0,0 +1,55 @@
+//
+// 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;
+
+ ///
+ /// Applies an "Lighten" blending to pixels.
+ ///
+ /// The type of the pixel
+ internal class DefaultLightenPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Gets the static instance of this blender.
+ ///
+ public static DefaultLightenPixelBlender Instance { get; } = new DefaultLightenPixelBlender();
+
+ ///
+ public override TPixel Blend(TPixel background, TPixel source, float amount)
+ {
+ return PorterDuffFunctions.LightenFunction(background, source, amount);
+ }
+
+ ///
+ public override void Blend(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
+
+ using (Buffer buffer = new Buffer(destination.Length * 3))
+ {
+ BufferSpan destinationSpan = buffer.Slice(0, destination.Length);
+ BufferSpan backgroundSpan = buffer.Slice(destination.Length, destination.Length);
+ BufferSpan sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
+
+ PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length);
+ PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length);
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ destinationSpan[i] = PorterDuffFunctions.LightenFunction(backgroundSpan[i], sourceSpan[i], amount[i]);
+ }
+
+ PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..7e0137018
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs
@@ -0,0 +1,55 @@
+//
+// 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;
+
+ ///
+ /// Applies an "Multiply" blending to pixels.
+ ///
+ /// The type of the pixel
+ internal class DefaultMultiplyPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Gets the static instance of this blender.
+ ///
+ public static DefaultMultiplyPixelBlender Instance { get; } = new DefaultMultiplyPixelBlender();
+
+ ///
+ public override TPixel Blend(TPixel background, TPixel source, float amount)
+ {
+ return PorterDuffFunctions.MultiplyFunction(background, source, amount);
+ }
+
+ ///
+ public override void Blend(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
+
+ using (Buffer buffer = new Buffer(destination.Length * 3))
+ {
+ BufferSpan destinationSpan = buffer.Slice(0, destination.Length);
+ BufferSpan backgroundSpan = buffer.Slice(destination.Length, destination.Length);
+ BufferSpan sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
+
+ PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length);
+ PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length);
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ destinationSpan[i] = PorterDuffFunctions.MultiplyFunction(backgroundSpan[i], sourceSpan[i], amount[i]);
+ }
+
+ PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..47bb084ca
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs
@@ -0,0 +1,55 @@
+//
+// 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;
+
+ ///
+ /// Applies a "Normal" otherwise nown as "Alpha Blending" blending to pixels.
+ ///
+ /// The type of the pixel
+ internal class DefaultNormalPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Gets the static instance of this blender.
+ ///
+ public static DefaultNormalPixelBlender Instance { get; } = new DefaultNormalPixelBlender();
+
+ ///
+ public override TPixel Blend(TPixel background, TPixel source, float amount)
+ {
+ return PorterDuffFunctions.NormalBlendFunction(background, source, amount);
+ }
+
+ ///
+ public override void Blend(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
+
+ using (Buffer buffer = new Buffer(destination.Length * 3))
+ {
+ BufferSpan destinationSpan = buffer.Slice(0, destination.Length);
+ BufferSpan backgroundSpan = buffer.Slice(destination.Length, destination.Length);
+ BufferSpan sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
+
+ PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length);
+ PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length);
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ destinationSpan[i] = PorterDuffFunctions.NormalBlendFunction(backgroundSpan[i], sourceSpan[i], amount[i]);
+ }
+
+ PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..fcb56e3dc
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs
@@ -0,0 +1,55 @@
+//
+// 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;
+
+ ///
+ /// Applies an "Overlay" blending to pixels.
+ ///
+ /// The type of the pixel
+ internal class DefaultOverlayPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Gets the static instance of this blender.
+ ///
+ public static DefaultOverlayPixelBlender Instance { get; } = new DefaultOverlayPixelBlender();
+
+ ///
+ public override TPixel Blend(TPixel background, TPixel source, float amount)
+ {
+ return PorterDuffFunctions.OverlayFunction(background, source, amount);
+ }
+
+ ///
+ public override void Blend(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
+ Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
+ Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
+
+ using (Buffer buffer = new Buffer(destination.Length * 3))
+ {
+ BufferSpan destinationSpan = buffer.Slice(0, destination.Length);
+ BufferSpan backgroundSpan = buffer.Slice(destination.Length, destination.Length);
+ BufferSpan sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
+
+ PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length);
+ PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length);
+
+ for (int i = 0; i < destination.Length; i++)
+ {
+ destinationSpan[i] = PorterDuffFunctions.OverlayFunction(backgroundSpan[i], sourceSpan[i], amount[i]);
+ }
+
+ PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs
new file mode 100644
index 000000000..df0de293c
--- /dev/null
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs
@@ -0,0 +1,55 @@
+//
+// 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;
+
+ ///
+ /// Applies an "Screen" blending to pixels.
+ ///
+ /// The type of the pixel
+ internal class DefaultScreenPixelBlender : PixelBlender
+ where TPixel : struct, IPixel
+ {
+ ///