diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
index 19ce469141..a96202b7b4 100644
--- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
@@ -72,26 +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 .
/// The options
- public RecolorBrushApplicator(PixelAccessor sourcePixels, TPixel sourceColor, TPixel targeTPixel, float threshold, GraphicsOptions 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);
@@ -120,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;
diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
index e58db46895..c49631de85 100644
--- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
@@ -74,9 +74,13 @@ namespace ImageSharp.Drawing.Processors
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);
+
int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
+ maxY = Math.Min(this.Location.Y + this.Size.Height, maxY);
+
int width = maxX - minX;
using (Buffer amount = new Buffer(width))
using (PixelAccessor toBlendPixels = targetImage.Lock())
@@ -94,7 +98,7 @@ namespace ImageSharp.Drawing.Processors
y =>
{
BufferSpan background = sourcePixels.GetRowSpan(y).Slice(minX, width);
- BufferSpan foreground = toBlendPixels.GetRowSpan(y - minY).Slice(0, width);
+ BufferSpan foreground = toBlendPixels.GetRowSpan(y - this.Location.Y).Slice(0, width);
this.blender.Blend(background, background, foreground, amount);
});
}
diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
index 124722cc09..3fd829549d 100644
--- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
@@ -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 0634a06a3e..25eccd245c 100644
--- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs
@@ -62,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 (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/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs
index c32103652b..f45582e1e6 100644
--- a/src/ImageSharp/GraphicsOptions.cs
+++ b/src/ImageSharp/GraphicsOptions.cs
@@ -60,7 +60,7 @@ namespace ImageSharp
///
public float BlendPercentage
{
- get => this.blendPercentage ?? 1;
+ get => (this.blendPercentage ?? 1).Clamp(0, 1);
set => this.blendPercentage = value;
}
diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
index 6b5126f720..25eb6a5c87 100644
--- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
+++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
@@ -9,7 +9,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using System.Runtime.CompilerServices;
///
- /// Collection of Porter Duff alpha blending functions
+ /// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model.
///
///
/// These functions are designed to be a general solution for all color cases,
@@ -19,7 +19,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
/// Note there are faster functions for when the backdrop color is known
/// to be opaque
///
- internal static class PorterDuffFunctions
+ internal static partial class PorterDuffFunctions
{
///
/// Source over backdrop
diff --git a/src/ImageSharp/PixelFormats/Rgba32.Transforms.cs b/src/ImageSharp/PixelFormats/Rgba32.Transforms.cs
deleted file mode 100644
index bd13a2de1a..0000000000
--- a/src/ImageSharp/PixelFormats/Rgba32.Transforms.cs
+++ /dev/null
@@ -1,254 +0,0 @@
-//
-// 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;
-
- ///
- /// Provides operators and composition algorithms.
- ///
- public partial struct Rgba32
- {
- ///
- /// Adds the second color to the first.
- ///
- /// The first source color.
- /// The second source color.
- ///
- /// The .
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Rgba32 operator +(Rgba32 left, Rgba32 right)
- {
- Vector4 add = left.ToVector4() + right.ToVector4();
- return PackNew(ref add);
- }
-
- ///
- /// Subtracts the second color from the first.
- ///
- /// The first source color.
- /// The second source color.
- ///
- /// The .
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Rgba32 operator -(Rgba32 left, Rgba32 right)
- {
- Vector4 sub = left.ToVector4() - right.ToVector4();
- return PackNew(ref sub);
- }
-
- ///
- /// The blending formula simply selects the source color.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 Normal(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 normal = Vector4BlendTransforms.Normal(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref normal);
- }
-
- ///
- /// Blends two colors by multiplication.
- ///
- /// The source color is multiplied by the destination color and replaces the destination.
- /// The resultant color is always at least as dark as either the source or destination color.
- /// Multiplying any color with black results in black. Multiplying any color with white preserves the
- /// original color.
- ///
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 Multiply(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 multiply = Vector4BlendTransforms.Multiply(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref multiply);
- }
-
- ///
- /// Multiplies the complements of the backdrop and source color values, then complements the result.
- ///
- /// The result color is always at least as light as either of the two constituent colors. Screening any
- /// color with white produces white; screening with black leaves the original color unchanged.
- /// The effect is similar to projecting multiple photographic slides simultaneously onto a single screen.
- ///
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 Screen(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 subtract = Vector4BlendTransforms.Screen(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref subtract);
- }
-
- ///
- /// Multiplies or screens the colors, depending on the source color value. The effect is similar to
- /// shining a harsh spotlight on the backdrop.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 HardLight(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 hardlight = Vector4BlendTransforms.HardLight(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref hardlight);
- }
-
- ///
- /// Multiplies or screens the colors, depending on the backdrop color value.
- ///
- /// Source colors overlay the backdrop while preserving its highlights and shadows.
- /// The backdrop color is not replaced but is mixed with the source color to reflect the lightness or darkness
- /// of the backdrop.
- ///
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 Overlay(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 overlay = Vector4BlendTransforms.Overlay(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref overlay);
- }
-
- ///
- /// Selects the darker of the backdrop and source colors.
- /// The backdrop is replaced with the source where the source is darker; otherwise, it is left unchanged.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 Darken(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 darken = Vector4BlendTransforms.Darken(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref darken);
- }
-
- ///
- /// Selects the lighter of the backdrop and source colors.
- /// The backdrop is replaced with the source where the source is lighter; otherwise, it is left unchanged.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 Lighten(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 lighten = Vector4BlendTransforms.Lighten(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref lighten);
- }
-
- ///
- /// Darkens or lightens the colors, depending on the source color value. The effect is similar to shining
- /// a diffused spotlight on the backdrop.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 SoftLight(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 softlight = Vector4BlendTransforms.SoftLight(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref softlight);
- }
-
- ///
- /// Brightens the backdrop color to reflect the source color. Painting with black produces no changes.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 ColorDodge(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 dodge = Vector4BlendTransforms.Dodge(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref dodge);
- }
-
- ///
- /// Darkens the backdrop color to reflect the source color. Painting with white produces no change.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 ColorBurn(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 burn = Vector4BlendTransforms.Burn(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref burn);
- }
-
- ///
- /// Subtracts the darker of the two constituent colors from the lighter color.
- /// Painting with white inverts the backdrop color; painting with black produces no change.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 Difference(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 difference = Vector4BlendTransforms.Difference(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref difference);
- }
-
- ///
- /// Produces an effect similar to that of the mode but lower in contrast. Painting with white
- /// inverts the backdrop color; painting with black produces no change
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static Rgba32 Exclusion(Rgba32 backdrop, Rgba32 source)
- {
- Vector4 exclusion = Vector4BlendTransforms.Exclusion(backdrop.ToVector4(), source.ToVector4());
- return PackNew(ref exclusion);
- }
-
- ///
- /// Linearly interpolates from one color to another based on the given weighting.
- ///
- /// The first color value.
- /// The second color value.
- ///
- /// 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
- ///
- public static Rgba32 Lerp(Rgba32 from, Rgba32 to, float amount)
- {
- Vector4 lerp = Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount);
- return PackNew(ref lerp);
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/RgbaVector.Transforms.cs b/src/ImageSharp/PixelFormats/RgbaVector.Transforms.cs
deleted file mode 100644
index fec1aa346e..0000000000
--- a/src/ImageSharp/PixelFormats/RgbaVector.Transforms.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-//
-// 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;
-
- ///
- /// Provides operators and composition algorithms.
- ///
- public partial struct RgbaVector
- {
- ///
- /// Adds the second color to the first.
- ///
- /// The first source color.
- /// The second source color.
- ///
- /// The .
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static RgbaVector operator +(RgbaVector left, RgbaVector right)
- {
- return new RgbaVector(left.backingVector + right.backingVector);
- }
-
- ///
- /// Subtracts the second color from the first.
- ///
- /// The first source color.
- /// The second source color.
- ///
- /// The .
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static RgbaVector operator -(RgbaVector left, RgbaVector right)
- {
- return new RgbaVector(left.backingVector - right.backingVector);
- }
-
- ///
- /// The blending formula simply selects the source color.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector Normal(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 normal = Vector4BlendTransforms.Normal(backdrop.backingVector, source.backingVector);
- return new RgbaVector(normal);
- }
-
- ///
- /// Blends two colors by multiplication.
- ///
- /// The source color is multiplied by the destination color and replaces the destination.
- /// The resultant color is always at least as dark as either the source or destination color.
- /// Multiplying any color with black results in black. Multiplying any color with white preserves the
- /// original color.
- ///
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector Multiply(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 multiply = Vector4BlendTransforms.Multiply(backdrop.backingVector, source.backingVector);
- return new RgbaVector(multiply);
- }
-
- ///
- /// Multiplies the complements of the backdrop and source color values, then complements the result.
- ///
- /// The result color is always at least as light as either of the two constituent colors. Screening any
- /// color with white produces white; screening with black leaves the original color unchanged.
- /// The effect is similar to projecting multiple photographic slides simultaneously onto a single screen.
- ///
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector Screen(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 subtract = Vector4BlendTransforms.Screen(backdrop.backingVector, source.backingVector);
- return new RgbaVector(subtract);
- }
-
- ///
- /// Multiplies or screens the colors, depending on the source color value. The effect is similar to
- /// shining a harsh spotlight on the backdrop.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector HardLight(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 hardlight = Vector4BlendTransforms.HardLight(backdrop.backingVector, source.backingVector);
- return new RgbaVector(hardlight);
- }
-
- ///
- /// Multiplies or screens the colors, depending on the backdrop color value.
- ///
- /// Source colors overlay the backdrop while preserving its highlights and shadows.
- /// The backdrop color is not replaced but is mixed with the source color to reflect the lightness or darkness
- /// of the backdrop.
- ///
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector Overlay(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 overlay = Vector4BlendTransforms.Overlay(backdrop.backingVector, source.backingVector);
- return new RgbaVector(overlay);
- }
-
- ///
- /// Selects the darker of the backdrop and source colors.
- /// The backdrop is replaced with the source where the source is darker; otherwise, it is left unchanged.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector Darken(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 darken = Vector4BlendTransforms.Darken(backdrop.backingVector, source.backingVector);
- return new RgbaVector(darken);
- }
-
- ///
- /// Selects the lighter of the backdrop and source colors.
- /// The backdrop is replaced with the source where the source is lighter; otherwise, it is left unchanged.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector Lighten(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 lighten = Vector4BlendTransforms.Lighten(backdrop.backingVector, source.backingVector);
- return new RgbaVector(lighten);
- }
-
- ///
- /// Darkens or lightens the colors, depending on the source color value. The effect is similar to shining
- /// a diffused spotlight on the backdrop.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector SoftLight(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 softlight = Vector4BlendTransforms.SoftLight(backdrop.backingVector, source.backingVector);
- return new RgbaVector(softlight);
- }
-
- ///
- /// Brightens the backdrop color to reflect the source color. Painting with black produces no changes.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector ColorDodge(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 dodge = Vector4BlendTransforms.Dodge(backdrop.backingVector, source.backingVector);
- return new RgbaVector(dodge);
- }
-
- ///
- /// Darkens the backdrop color to reflect the source color. Painting with white produces no change.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector ColorBurn(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 burn = Vector4BlendTransforms.Burn(backdrop.backingVector, source.backingVector);
- return new RgbaVector(burn);
- }
-
- ///
- /// Subtracts the darker of the two constituent colors from the lighter color.
- /// Painting with white inverts the backdrop color; painting with black produces no change.
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector Difference(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 difference = Vector4BlendTransforms.Difference(backdrop.backingVector, source.backingVector);
- return new RgbaVector(difference);
- }
-
- ///
- /// Produces an effect similar to that of the mode but lower in contrast. Painting with white
- /// inverts the backdrop color; painting with black produces no change
- ///
- /// The backdrop color.
- /// The source color.
- ///
- /// The .
- ///
- public static RgbaVector Exclusion(RgbaVector backdrop, RgbaVector source)
- {
- Vector4 exclusion = Vector4BlendTransforms.Exclusion(backdrop.backingVector, source.backingVector);
- return new RgbaVector(exclusion);
- }
-
- ///
- /// Linearly interpolates from one color to another based on the given weighting.
- ///
- /// The first color value.
- /// The second color value.
- ///
- /// 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
- ///
- public static RgbaVector Lerp(RgbaVector from, RgbaVector to, float amount)
- {
- return new RgbaVector(Vector4.Lerp(from.backingVector, to.backingVector, amount));
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs b/src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs
deleted file mode 100644
index f066e9a816..0000000000
--- a/src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs
+++ /dev/null
@@ -1,292 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.PixelFormats
-{
- using System.Numerics;
-
- ///
- /// Transform algorithms that match the equations defined in the W3C Compositing and Blending Level 1 specification.
- ///
- ///
- internal class Vector4BlendTransforms
- {
- ///
- /// The blending formula simply selects the source vector.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Normal(Vector4 backdrop, Vector4 source)
- {
- return new Vector4(source.X, source.Y, source.Z, source.W);
- }
-
- ///
- /// Blends two vectors by multiplication.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Multiply(Vector4 backdrop, Vector4 source)
- {
- Vector4 multiply = backdrop * source;
- multiply.W = backdrop.W;
- return multiply;
- }
-
- ///
- /// Multiplies the complements of the backdrop and source vector values, then complements the result.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Screen(Vector4 backdrop, Vector4 source)
- {
- Vector4 subtract = backdrop + source - (backdrop * source);
- subtract.W = backdrop.W;
- return subtract;
- }
-
- ///
- /// Multiplies or screens the colors, depending on the source vector value.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 HardLight(Vector4 backdrop, Vector4 source)
- {
- return new Vector4(BlendOverlay(source.X, backdrop.X), BlendOverlay(source.Y, backdrop.Y), BlendOverlay(source.Z, backdrop.Z), backdrop.W);
- }
-
- ///
- /// Multiplies or screens the vectors, depending on the backdrop vector value.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Overlay(Vector4 backdrop, Vector4 source)
- {
- return new Vector4(BlendOverlay(backdrop.X, source.X), BlendOverlay(backdrop.Y, source.Y), BlendOverlay(backdrop.Z, source.Z), backdrop.W);
- }
-
- ///
- /// Selects the minimum of the backdrop and source vectors.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Darken(Vector4 backdrop, Vector4 source)
- {
- Vector4 result = Vector4.Min(backdrop, source);
- result.W = backdrop.W;
- return result;
- }
-
- ///
- /// Selects the max of the backdrop and source vector.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Lighten(Vector4 backdrop, Vector4 source)
- {
- Vector4 result = Vector4.Max(backdrop, source);
- result.W = backdrop.W;
- return result;
- }
-
- ///
- /// Selects the maximum or minimum of the vectors, depending on the source vector value.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 SoftLight(Vector4 backdrop, Vector4 source)
- {
- return new Vector4(BlendSoftLight(backdrop.X, source.X), BlendSoftLight(backdrop.Y, source.Y), BlendSoftLight(backdrop.Z, source.Z), backdrop.W);
- }
-
- ///
- /// Increases the backdrop vector to reflect the source vector.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Dodge(Vector4 backdrop, Vector4 source)
- {
- return new Vector4(BlendDodge(backdrop.X, source.X), BlendDodge(backdrop.Y, source.Y), BlendDodge(backdrop.Z, source.Z), backdrop.W);
- }
-
- ///
- /// Decreases the backdrop vector to reflect the source vector.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Burn(Vector4 backdrop, Vector4 source)
- {
- return new Vector4(BlendBurn(backdrop.X, source.X), BlendBurn(backdrop.Y, source.Y), BlendBurn(backdrop.Z, source.Z), backdrop.W);
- }
-
- ///
- /// Subtracts the minimum of the two constituent vectors from the maximum vector.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Difference(Vector4 backdrop, Vector4 source)
- {
- Vector4 result = Vector4.Abs(backdrop - source);
- result.W = backdrop.W;
- return result;
- }
-
- ///
- /// Produces an effect similar to that of the mode but lower in magnitude.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// The .
- ///
- public static Vector4 Exclusion(Vector4 backdrop, Vector4 source)
- {
- return new Vector4(BlendExclusion(backdrop.X, source.X), BlendExclusion(backdrop.Y, source.Y), BlendExclusion(backdrop.Z, source.Z), backdrop.W);
- }
-
- ///
- /// Linearly interpolates from one vector to another based on the given weighting.
- /// The two vectors are premultiplied before operating.
- ///
- /// The backdrop vector.
- /// The source vector.
- ///
- /// 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
- ///
- public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount)
- {
- amount = amount.Clamp(0, 1);
-
- // Santize on zero alpha
- if (MathF.Abs(backdrop.W) < Constants.Epsilon)
- {
- source.W *= amount;
- return source;
- }
-
- if (MathF.Abs(source.W) < Constants.Epsilon)
- {
- return backdrop;
- }
-
- // Premultiply the source vector.
- // Oddly premultiplying the background vector creates dark outlines when pixels
- // Have low alpha values.
- source = new Vector4(source.X, source.Y, source.Z, 1) * (source.W * amount);
-
- // This should be implementing the following formula
- // https://en.wikipedia.org/wiki/Alpha_compositing
- // Vout = Vs + Vb (1 - Vsa)
- // Aout = Vsa + Vsb (1 - Vsa)
- Vector3 inverseW = new Vector3(1 - source.W);
- Vector3 xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z);
- Vector3 xyzS = new Vector3(source.X, source.Y, source.Z);
-
- return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W)));
- }
-
- ///
- /// Multiplies or screens the backdrop component, depending on the component value.
- ///
- /// The backdrop component.
- /// The source component.
- ///
- /// The .
- ///
- private static float BlendOverlay(float b, float s)
- {
- return b <= .5F ? (2F * b * s) : (1F - (2F * (1F - b) * (1F - s)));
- }
-
- ///
- /// Darkens or lightens the backdrop component, depending on the source component value.
- ///
- /// The backdrop component.
- /// The source component.
- ///
- /// The .
- ///
- private static float BlendSoftLight(float b, float s)
- {
- return s <= .5F ? ((2F * b * s) + (b * b * (1F - (2F * s)))) : (MathF.Sqrt(b) * ((2F * s) - 1F)) + (2F * b * (1F - s));
- }
-
- ///
- /// Brightens the backdrop component to reflect the source component.
- ///
- /// The backdrop component.
- /// The source component.
- ///
- /// The .
- ///
- private static float BlendDodge(float b, float s)
- {
- return MathF.Abs(s - 1F) < Constants.Epsilon ? s : MathF.Min(b / (1F - s), 1F);
- }
-
- ///
- /// Darkens the backdrop component to reflect the source component.
- ///
- /// The backdrop component.
- /// The source component.
- ///
- /// The .
- ///
- private static float BlendBurn(float b, float s)
- {
- return MathF.Abs(s) < Constants.Epsilon ? s : MathF.Max(1F - ((1F - b) / s), 0F);
- }
-
- ///
- /// Darkens the backdrop component to reflect the source component.
- ///
- /// The backdrop component.
- /// The source component.
- ///
- /// The .
- ///
- private static float BlendExclusion(float b, float s)
- {
- return b + s - (2F * b * s);
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs
index 3299add7f7..937dca9ba8 100644
--- a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs
@@ -26,7 +26,7 @@ namespace ImageSharp
public static Image Lomograph(this Image source)
where TPixel : struct, IPixel
{
- return Lomograph(source, source.Bounds);
+ return Lomograph(source, source.Bounds, GraphicsOptions.Default);
}
///
@@ -41,7 +41,36 @@ namespace ImageSharp
public static Image Lomograph(this Image source, Rectangle rectangle)
where TPixel : struct, IPixel
{
- source.ApplyProcessor(new LomographProcessor(), rectangle);
+ return Lomograph(source, rectangle, GraphicsOptions.Default);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Lomograph camera effect.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Lomograph(this Image source, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return Lomograph(source, source.Bounds, options);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Lomograph camera effect.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Lomograph(this Image source, Rectangle rectangle, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ source.ApplyProcessor(new LomographProcessor(options), rectangle);
return source;
}
}
diff --git a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs
index 194800ec72..f1a573c05d 100644
--- a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs
@@ -26,7 +26,7 @@ namespace ImageSharp
public static Image Polaroid(this Image source)
where TPixel : struct, IPixel
{
- return Polaroid(source, source.Bounds);
+ return Polaroid(source, GraphicsOptions.Default);
}
///
@@ -41,7 +41,36 @@ namespace ImageSharp
public static Image Polaroid(this Image source, Rectangle rectangle)
where TPixel : struct, IPixel
{
- source.ApplyProcessor(new PolaroidProcessor(), rectangle);
+ return Polaroid(source, rectangle, GraphicsOptions.Default);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Polaroid camera effect.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Polaroid(this Image source, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return Polaroid(source, source.Bounds, options);
+ }
+
+ ///
+ /// Alters the colors of the image recreating an old Polaroid camera effect.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Polaroid(this Image source, Rectangle rectangle, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ source.ApplyProcessor(new PolaroidProcessor(options), rectangle);
return source;
}
}
diff --git a/src/ImageSharp/Processing/Effects/BackgroundColor.cs b/src/ImageSharp/Processing/Effects/BackgroundColor.cs
index a1914fee32..f52cf1cb2a 100644
--- a/src/ImageSharp/Processing/Effects/BackgroundColor.cs
+++ b/src/ImageSharp/Processing/Effects/BackgroundColor.cs
@@ -16,6 +16,38 @@ namespace ImageSharp
///
public static partial class ImageExtensions
{
+ ///
+ /// Replaces the background color of image with the given one.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The color to set as the background.
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image BackgroundColor(this Image source, TPixel color, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return BackgroundColor(source, color, source.Bounds, options);
+ }
+
+ ///
+ /// Replaces the background color of image with the given one.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The color to set as the background.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle);
+ return source;
+ }
+
///
/// Replaces the background color of image with the given one.
///
@@ -26,7 +58,7 @@ namespace ImageSharp
public static Image BackgroundColor(this Image source, TPixel color)
where TPixel : struct, IPixel
{
- return BackgroundColor(source, color, source.Bounds);
+ return BackgroundColor(source, color, GraphicsOptions.Default);
}
///
@@ -42,8 +74,7 @@ namespace ImageSharp
public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle)
where TPixel : struct, IPixel
{
- source.ApplyProcessor(new BackgroundColorProcessor(color), rectangle);
- return source;
+ return BackgroundColor(source, color, rectangle, GraphicsOptions.Default);
}
}
}
diff --git a/src/ImageSharp/Processing/Overlays/Vignette.cs b/src/ImageSharp/Processing/Overlays/Vignette.cs
index f805dd07a0..2eaf2e1efa 100644
--- a/src/ImageSharp/Processing/Overlays/Vignette.cs
+++ b/src/ImageSharp/Processing/Overlays/Vignette.cs
@@ -25,7 +25,7 @@ namespace ImageSharp
public static Image Vignette(this Image source)
where TPixel : struct, IPixel
{
- return Vignette(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds);
+ return Vignette(source, GraphicsOptions.Default);
}
///
@@ -38,7 +38,7 @@ namespace ImageSharp
public static Image Vignette(this Image source, TPixel color)
where TPixel : struct, IPixel
{
- return Vignette(source, color, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds);
+ return Vignette(source, color, GraphicsOptions.Default);
}
///
@@ -52,7 +52,7 @@ namespace ImageSharp
public static Image Vignette(this Image source, float radiusX, float radiusY)
where TPixel : struct, IPixel
{
- return Vignette(source, NamedColors.Black, radiusX, radiusY, source.Bounds);
+ return Vignette(source, radiusX, radiusY, GraphicsOptions.Default);
}
///
@@ -67,7 +67,7 @@ namespace ImageSharp
public static Image Vignette(this Image source, Rectangle rectangle)
where TPixel : struct, IPixel
{
- return Vignette(source, NamedColors.Black, 0, 0, rectangle);
+ return Vignette(source, rectangle, GraphicsOptions.Default);
}
///
@@ -85,7 +85,84 @@ namespace ImageSharp
public static Image Vignette(this Image source, TPixel color, float radiusX, float radiusY, Rectangle rectangle)
where TPixel : struct, IPixel
{
- VignetteProcessor processor = new VignetteProcessor(color) { RadiusX = radiusX, RadiusY = radiusY };
+ return Vignette(source, color, radiusX, radiusY, rectangle, GraphicsOptions.Default);
+ }
+
+ ///
+ /// Applies a radial vignette effect to an image.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Vignette(this Image source, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return Vignette(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds, options);
+ }
+
+ ///
+ /// Applies a radial vignette effect to an image.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The color to set as the vignette.
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Vignette(this Image source, TPixel color, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return Vignette(source, color, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds, options);
+ }
+
+ ///
+ /// Applies a radial vignette effect to an image.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The the x-radius.
+ /// The the y-radius.
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Vignette(this Image source, float radiusX, float radiusY, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return Vignette(source, NamedColors.Black, radiusX, radiusY, source.Bounds, options);
+ }
+
+ ///
+ /// Applies a radial vignette effect to an image.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Vignette(this Image source, Rectangle rectangle, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return Vignette(source, NamedColors.Black, 0, 0, rectangle, options);
+ }
+
+ ///
+ /// Applies a radial vignette effect to an image.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The color to set as the vignette.
+ /// The the x-radius.
+ /// The the y-radius.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The options effecting pixel blending.
+ /// The .
+ public static Image Vignette(this Image source, TPixel color, float radiusX, float radiusY, Rectangle rectangle, GraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ VignetteProcessor processor = new VignetteProcessor(color, options) { RadiusX = radiusX, RadiusY = radiusY };
source.ApplyProcessor(processor, rectangle);
return source;
}
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs
index 70b9979972..f6480c1837 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs
@@ -18,6 +18,16 @@ namespace ImageSharp.Processing.Processors
where TPixel : struct, IPixel
{
private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255);
+ private GraphicsOptions options;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options effecting blending and composition.
+ public LomographProcessor(GraphicsOptions options)
+ {
+ this.options = options;
+ }
///
public override Matrix4x4 Matrix => new Matrix4x4()
@@ -34,7 +44,7 @@ namespace ImageSharp.Processing.Processors
///
protected override void AfterApply(ImageBase source, Rectangle sourceRectangle)
{
- new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle);
+ new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
index c06275314b..5df034add2 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
@@ -19,6 +19,16 @@ namespace ImageSharp.Processing.Processors
{
private static TPixel veryDarkOrange = ColorBuilder.FromRGB(102, 34, 0);
private static TPixel lightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178);
+ private GraphicsOptions options;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options effecting blending and composition.
+ public PolaroidProcessor(GraphicsOptions options)
+ {
+ this.options = options;
+ }
///
public override Matrix4x4 Matrix => new Matrix4x4()
@@ -41,8 +51,8 @@ namespace ImageSharp.Processing.Processors
///
protected override void AfterApply(ImageBase source, Rectangle sourceRectangle)
{
- new VignetteProcessor(veryDarkOrange).Apply(source, sourceRectangle);
- new GlowProcessor(lightOrange, GraphicsOptions.Default) { Radius = source.Width / 4F }.Apply(source, sourceRectangle);
+ new VignetteProcessor(veryDarkOrange, this.options).Apply(source, sourceRectangle);
+ new GlowProcessor(lightOrange, this.options) { Radius = source.Width / 4F }.Apply(source, sourceRectangle);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs
index 21973de3e4..511a810b27 100644
--- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs
@@ -18,13 +18,17 @@ namespace ImageSharp.Processing.Processors
internal class BackgroundColorProcessor : ImageProcessor
where TPixel : struct, IPixel
{
+ private readonly GraphicsOptions options;
+
///
/// Initializes a new instance of the class.
///
/// The to set the background color to.
- public BackgroundColorProcessor(TPixel color)
+ /// The options defining blending algorithum and amount.
+ public BackgroundColorProcessor(TPixel color, GraphicsOptions options)
{
this.Value = color;
+ this.options = options;
}
///
@@ -57,10 +61,19 @@ namespace ImageSharp.Processing.Processors
startY = 0;
}
- Vector4 backgroundColor = this.Value.ToVector4();
+ int width = maxX - minX;
+ using (Buffer colors = new Buffer(width))
+ using (Buffer amount = new Buffer(width))
using (PixelAccessor sourcePixels = source.Lock())
{
+ for (int i = 0; i < width; i++)
+ {
+ colors[i] = this.Value;
+ amount[i] = this.options.BlendPercentage;
+ }
+
+ PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode);
Parallel.For(
minY,
maxY,
@@ -68,26 +81,11 @@ namespace ImageSharp.Processing.Processors
y =>
{
int offsetY = y - startY;
- for (int x = minX; x < maxX; x++)
- {
- int offsetX = x - startX;
- Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
- float a = color.W;
-
- if (a < 1 && a > 0)
- {
- color = Vector4BlendTransforms.PremultipliedLerp(backgroundColor, color, .5F);
- }
- if (MathF.Abs(a) < Constants.Epsilon)
- {
- color = backgroundColor;
- }
+ BufferSpan destination = sourcePixels.GetRowSpan(offsetY).Slice(minX - startX, width);
- TPixel packed = default(TPixel);
- packed.PackFromVector4(color);
- sourcePixels[offsetX, offsetY] = packed;
- }
+ // this switched color & destination in the 2nd and 3rd places because we are applying the target colour under the current one
+ blender.Blend(destination, colors, destination, amount);
});
}
}
diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
index 31e813564b..d698b543c4 100644
--- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
@@ -18,13 +18,20 @@ namespace ImageSharp.Processing.Processors
internal class VignetteProcessor : ImageProcessor
where TPixel : struct, IPixel
{
+ private readonly GraphicsOptions options;
+ private readonly PixelBlender blender;
+
///
/// Initializes a new instance of the class.
///
/// The color of the vignette.
- public VignetteProcessor(TPixel color)
+ /// The options effecting blending and composition.
+ public VignetteProcessor(TPixel color, GraphicsOptions options)
{
this.VignetteColor = color;
+
+ this.options = options;
+ this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode);
}
///
@@ -72,23 +79,34 @@ namespace ImageSharp.Processing.Processors
startY = 0;
}
+ int width = maxX - minX;
+ using (Buffer rowColors = new Buffer(width))
using (PixelAccessor sourcePixels = source.Lock())
{
+ for (int i = 0; i < width; i++)
+ {
+ rowColors[i] = vignetteColor;
+ }
+
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
- int offsetY = y - startY;
- for (int x = minX; x < maxX; x++)
+ using (Buffer amounts = new Buffer(width))
{
- int offsetX = x - startX;
- float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
- Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
- TPixel packed = default(TPixel);
- packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, vignetteColor.ToVector4(), .9F * (distance / maxDistance)));
- sourcePixels[offsetX, offsetY] = packed;
+ int offsetY = y - startY;
+ int offsetX = minX - startX;
+ for (int i = 0; i < width; i++)
+ {
+ float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY));
+ amounts[i] = (this.options.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1);
+ }
+
+ BufferSpan destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width);
+
+ this.blender.Blend(destination, destination, rowColors, amounts);
}
});
}
diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs
index 748bbf4fad..6daf120fac 100644
--- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs
+++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs
@@ -122,12 +122,43 @@ namespace ImageSharp.Benchmarks
float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
TPixel packed = default(TPixel);
- packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance))));
+ packed.PackFromVector4(PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance))));
sourcePixels[offsetX, offsetY] = packed;
}
});
}
}
+ public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount)
+ {
+ amount = amount.Clamp(0, 1);
+
+ // Santize on zero alpha
+ if (MathF.Abs(backdrop.W) < Constants.Epsilon)
+ {
+ source.W *= amount;
+ return source;
+ }
+
+ if (MathF.Abs(source.W) < Constants.Epsilon)
+ {
+ return backdrop;
+ }
+
+ // Premultiply the source vector.
+ // Oddly premultiplying the background vector creates dark outlines when pixels
+ // Have low alpha values.
+ source = new Vector4(source.X, source.Y, source.Z, 1) * (source.W * amount);
+
+ // This should be implementing the following formula
+ // https://en.wikipedia.org/wiki/Alpha_compositing
+ // Vout = Vs + Vb (1 - Vsa)
+ // Aout = Vsa + Vsb (1 - Vsa)
+ Vector3 inverseW = new Vector3(1 - source.W);
+ Vector3 xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z);
+ Vector3 xyzS = new Vector3(source.X, source.Y, source.Z);
+
+ return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W)));
+ }
}
}
}
diff --git a/tests/ImageSharp.Tests/Colors/Rgba32TransformTests.cs b/tests/ImageSharp.Tests/Colors/Rgba32TransformTests.cs
deleted file mode 100644
index 8d5e973b13..0000000000
--- a/tests/ImageSharp.Tests/Colors/Rgba32TransformTests.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Tests.Colors
-{
- using ImageSharp.PixelFormats;
-
- using Xunit;
-
- ///
- /// Tests the color transform algorithms. Test results match the output of CSS equivalents.
- ///
- ///
- public class ColorTransformTests
- {
- ///
- /// Orange backdrop
- ///
- private static readonly Rgba32 Backdrop = new Rgba32(204, 102, 0);
-
- ///
- /// Blue source
- ///
- private static readonly Rgba32 Source = new Rgba32(0, 102, 153);
-
- [Fact]
- public void Normal()
- {
- Rgba32 normal = Rgba32.Normal(Backdrop, Source);
- Assert.True(normal == Source);
- }
-
- [Fact]
- public void Multiply()
- {
- Assert.True(Rgba32.Multiply(Backdrop, Rgba32.Black) == Rgba32.Black);
- Assert.True(Rgba32.Multiply(Backdrop, Rgba32.White) == Backdrop);
-
- Rgba32 multiply = Rgba32.Multiply(Backdrop, Source);
- Assert.True(multiply == new Rgba32(0, 41, 0));
- }
-
- [Fact]
- public void Screen()
- {
- Assert.True(Rgba32.Screen(Backdrop, Rgba32.Black) == Backdrop);
- Assert.True(Rgba32.Screen(Backdrop, Rgba32.White) == Rgba32.White);
-
- Rgba32 screen = Rgba32.Screen(Backdrop, Source);
- Assert.True(screen == new Rgba32(204, 163, 153));
- }
-
- [Fact]
- public void HardLight()
- {
- Rgba32 hardLight = Rgba32.HardLight(Backdrop, Source);
- Assert.True(hardLight == new Rgba32(0, 82, 51));
- }
-
- [Fact]
- public void Overlay()
- {
- Rgba32 overlay = Rgba32.Overlay(Backdrop, Source);
- Assert.True(overlay == new Rgba32(153, 82, 0));
- }
-
- [Fact]
- public void Darken()
- {
- Rgba32 darken = Rgba32.Darken(Backdrop, Source);
- Assert.True(darken == new Rgba32(0, 102, 0));
- }
-
- [Fact]
- public void Lighten()
- {
- Rgba32 lighten = Rgba32.Lighten(Backdrop, Source);
- Assert.True(lighten == new Rgba32(204, 102, 153));
- }
-
- [Fact]
- public void SoftLight()
- {
- Rgba32 softLight = Rgba32.SoftLight(Backdrop, Source);
- Assert.True(softLight == new Rgba32(163, 90, 0));
- }
-
- [Fact]
- public void ColorDodge()
- {
- Rgba32 colorDodge = Rgba32.ColorDodge(Backdrop, Source);
- Assert.True(colorDodge == new Rgba32(204, 170, 0));
- }
-
- [Fact]
- public void ColorBurn()
- {
- Rgba32 colorBurn = Rgba32.ColorBurn(Backdrop, Source);
- Assert.True(colorBurn == new Rgba32(0, 0, 0));
- }
-
- [Fact]
- public void Difference()
- {
- Rgba32 difference = Rgba32.Difference(Backdrop, Source);
- Assert.True(difference == new Rgba32(204, 0, 153));
- }
-
- [Fact]
- public void Exclusion()
- {
- Rgba32 exclusion = Rgba32.Exclusion(Backdrop, Source);
- Assert.True(exclusion == new Rgba32(204, 122, 153));
- }
- }
-}
diff --git a/tests/ImageSharp.Tests/Colors/RgbaVectorTransformTests.cs b/tests/ImageSharp.Tests/Colors/RgbaVectorTransformTests.cs
deleted file mode 100644
index 81cbb63c41..0000000000
--- a/tests/ImageSharp.Tests/Colors/RgbaVectorTransformTests.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Tests.Colors
-{
- using ImageSharp.PixelFormats;
- using Xunit;
-
- ///
- /// Tests the color transform algorithms. Test results match the output of CSS equivalents.
- ///
- ///
- public class RgbaVectorTransformTests
- {
- private static readonly ApproximateFloatComparer FloatComparer = new ApproximateFloatComparer(0.01F);
-
- ///
- /// Orange backdrop
- ///
- private static readonly RgbaVector Backdrop = new RgbaVector(204, 102, 0);
-
- ///
- /// Blue source
- ///
- private static readonly RgbaVector Source = new RgbaVector(0, 102, 153);
-
- [Fact]
- public void Normal()
- {
- RgbaVector normal = RgbaVector.Normal(Backdrop, Source);
- Assert.True(normal == Source);
- }
-
- // TODO: These tests keep sporadically breaking our builds. Fins out why they work locally but not on the CI.
- // [Fact]
- // public void Multiply()
- // {
- // Assert.Equal(RgbaVector.Multiply(Backdrop, RgbaVector.Black).ToVector4(), RgbaVector.Black.ToVector4(), FloatComparer);
- // Assert.Equal(RgbaVector.Multiply(Backdrop, RgbaVector.White).ToVector4(), Backdrop.ToVector4(), FloatComparer);
-
- // RgbaVector multiply = RgbaVector.Multiply(Backdrop, Source);
- // Assert.Equal(multiply.ToVector4(), new RgbaVector(0, 41, 0).ToVector4(), FloatComparer);
- // }
-
- // [Fact]
- // public void Screen()
- // {
- // Assert.Equal(RgbaVector.Screen(Backdrop, RgbaVector.Black).ToVector4(), Backdrop.ToVector4(), FloatComparer);
- // Assert.Equal(RgbaVector.Screen(Backdrop, RgbaVector.White).ToVector4(), RgbaVector.White.ToVector4(), FloatComparer);
-
- // RgbaVector screen = RgbaVector.Screen(Backdrop, Source);
- // Assert.Equal(screen.ToVector4(), new RgbaVector(204, 163, 153).ToVector4(), FloatComparer);
- // }
-
- [Fact]
- public void HardLight()
- {
- RgbaVector hardLight = RgbaVector.HardLight(Backdrop, Source);
- Assert.Equal(hardLight.ToVector4(), new RgbaVector(0, 82, 51).ToVector4(), FloatComparer);
- }
-
- [Fact]
- public void Overlay()
- {
- RgbaVector overlay = RgbaVector.Overlay(Backdrop, Source);
- Assert.Equal(overlay.ToVector4(), new RgbaVector(153, 82, 0).ToVector4(), FloatComparer);
- }
-
- [Fact]
- public void Darken()
- {
- RgbaVector darken = RgbaVector.Darken(Backdrop, Source);
- Assert.Equal(darken.ToVector4(), new RgbaVector(0, 102, 0).ToVector4(), FloatComparer);
- }
-
- [Fact]
- public void Lighten()
- {
- RgbaVector lighten = RgbaVector.Lighten(Backdrop, Source);
- Assert.Equal(lighten.ToVector4(), new RgbaVector(204, 102, 153).ToVector4(), FloatComparer);
- }
-
- [Fact]
- public void SoftLight()
- {
- RgbaVector softLight = RgbaVector.SoftLight(Backdrop, Source);
- Assert.Equal(softLight.ToVector4(), new RgbaVector(163, 90, 0).ToVector4(), FloatComparer);
- }
-
- [Fact]
- public void ColorDodge()
- {
- RgbaVector colorDodge = RgbaVector.ColorDodge(Backdrop, Source);
- Assert.Equal(colorDodge.ToVector4(), new RgbaVector(204, 170, 0).ToVector4(), FloatComparer);
- }
-
- [Fact]
- public void ColorBurn()
- {
- RgbaVector colorBurn = RgbaVector.ColorBurn(Backdrop, Source);
- Assert.Equal(colorBurn.ToVector4(), new RgbaVector(0, 0, 0).ToVector4(), FloatComparer);
- }
-
- [Fact]
- public void Difference()
- {
- RgbaVector difference = RgbaVector.Difference(Backdrop, Source);
- Assert.Equal(difference.ToVector4(), new RgbaVector(204, 0, 153).ToVector4(), FloatComparer);
- }
-
- [Fact]
- public void Exclusion()
- {
- RgbaVector exclusion = RgbaVector.Exclusion(Backdrop, Source);
- Assert.Equal(exclusion.ToVector4(), new RgbaVector(204, 122, 153).ToVector4(), FloatComparer);
- }
- }
-}
diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs
deleted file mode 100644
index 885029fdff..0000000000
--- a/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Tests
-{
- using System.IO;
- using ImageSharp.Drawing;
- using ImageSharp.PixelFormats;
- using Xunit;
-
- public class DrawImageEffectTest : FileTestBase
- {
- [Fact]
- public void ImageShouldApplyDrawImageFilter()
- {
- string path = this.CreateOutputDirectory("Drawing", "DrawImageEffect");
-
- PixelBlenderMode[] modes = (PixelBlenderMode[])System.Enum.GetValues(typeof(PixelBlenderMode));
-
- using (Image blend = TestFile.Create(TestImages.Png.Blur).CreateImage())
- {
- foreach (TestFile file in Files)
- {
- using (Image image = file.CreateImage())
- {
- foreach (PixelBlenderMode mode in modes)
- {
- using (FileStream output = File.OpenWrite($"{path}/{mode}.{file.FileName}"))
- {
- Size size = new Size(image.Width / 2, image.Height / 2);
- Point loc = new Point(image.Width / 4, image.Height / 4);
-
- image.DrawImage(blend, size, loc, new GraphicsOptions() {
- BlenderMode = mode,
- BlendPercentage = .75f
- }).Save(output);
- }
- }
- }
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
index f87a6170d9..030034a8f2 100644
--- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
+++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
@@ -6,30 +6,42 @@
namespace ImageSharp.Tests
{
using System.IO;
-
+ using System.Linq;
+ using ImageSharp.PixelFormats;
using Xunit;
public class DrawImageTest : FileTestBase
{
- [Fact]
- public void ImageShouldApplyDrawImageFilter()
- {
- string path = this.CreateOutputDirectory("Drawing", "DrawImage");
+ private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass;
+
+ public static readonly string[] TestFiles = {
+ TestImages.Jpeg.Baseline.Calliphora,
+ TestImages.Bmp.Car,
+ TestImages.Png.Splash,
+ TestImages.Gif.Rings
+ };
- using (Image blend = TestFile.Create(TestImages.Bmp.Car).CreateImage())
+ object[][] Modes = System.Enum.GetValues(typeof(PixelBlenderMode)).Cast().Select(x => new object[] { x }).ToArray();
+
+ [Theory]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Multiply)]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Add)]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Substract)]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Screen)]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Darken)]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Lighten)]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Overlay)]
+ [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.HardLight)]
+ public void ImageShouldApplyDrawImage(TestImageProvider provider, PixelBlenderMode mode)
+ where TPixel : struct, IPixel
+ {
+ using (Image image = provider.GetImage())
+ using (Image blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes))
{
- foreach (TestFile file in Files)
- {
- using (Image image = file.CreateImage())
- {
- using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
- {
- image.DrawImage(blend, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4))
- .Save(output);
- }
- }
- }
+ image.DrawImage(blend, mode, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4))
+ .DebugSave(provider, new { mode });
}
}
}
-}
\ No newline at end of file
+}