diff --git a/src/ImageSharp/Colors/Vector4BlendTransforms.cs b/src/ImageSharp/Colors/Vector4BlendTransforms.cs
index ca8ac55c02..b1d6ebb8c9 100644
--- a/src/ImageSharp/Colors/Vector4BlendTransforms.cs
+++ b/src/ImageSharp/Colors/Vector4BlendTransforms.cs
@@ -17,7 +17,7 @@ namespace ImageSharp
///
/// The epsilon for comparing floating point numbers.
///
- private const float Epsilon = 0.001F;
+ private const float Epsilon = 0.0001F;
///
/// The blending formula simply selects the source vector.
@@ -187,7 +187,7 @@ namespace ImageSharp
///
/// Linearly interpolates from one vector to another based on the given weighting.
- /// The two vectors are premultiplied by their W component before operating.
+ /// The two vectors are premultiplied before operating.
///
/// The backdrop vector.
/// The source vector.
@@ -195,20 +195,43 @@ namespace ImageSharp
/// 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);
- backdrop = backdrop * new Vector4(backdrop.X, backdrop.Y, backdrop.Z, 1) * backdrop.W;
- source = source * new Vector4(source.X, source.Y, source.Z, 1) * source.W;
- return Vector4.Lerp(backdrop, source, amount) / new Vector4(source.W, source.W, source.W, 1);
+ // Santize on zero alpha
+ if (Math.Abs(backdrop.W) < Epsilon)
+ {
+ source.W *= amount;
+ return source;
+ }
+
+ if (Math.Abs(source.W) < 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 color component, depending on the component value.
+ /// Multiplies or screens the backdrop component, depending on the component value.
///
/// The backdrop component.
/// The source component.
diff --git a/src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs
index ab077d6987..cc81179f80 100644
--- a/src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs
+++ b/src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs
@@ -79,7 +79,7 @@ namespace ImageSharp.Processors
if (a < 1 && a > 0)
{
- color = Vector4.Lerp(color, backgroundColor, .5F);
+ color = Vector4BlendTransforms.PremultipliedLerp(backgroundColor, color, .5F);
}
if (Math.Abs(a) < Epsilon)
diff --git a/src/ImageSharp/Filters/Processors/Overlays/BlendProcessor.cs b/src/ImageSharp/Filters/Processors/Overlays/BlendProcessor.cs
index 3592c7921e..e764879763 100644
--- a/src/ImageSharp/Filters/Processors/Overlays/BlendProcessor.cs
+++ b/src/ImageSharp/Filters/Processors/Overlays/BlendProcessor.cs
@@ -82,17 +82,14 @@ namespace ImageSharp.Processors
{
for (int x = minX; x < maxX; x++)
{
- Vector4 color = sourcePixels[x, y].ToVector4();
- Vector4 blendedColor = toBlendPixels[x - minX, y - minY].ToVector4();
+ Vector4 backgroundVector = sourcePixels[x, y].ToVector4();
+ Vector4 sourceVector = toBlendPixels[x - minX, y - minY].ToVector4();
- if (blendedColor.W > 0)
- {
- // Lerping colors is dependent on the alpha of the blended color
- color = Vector4BlendTransforms.PremultipliedLerp(color, blendedColor, alpha);
- }
+ // Lerping colors is dependent on the alpha of the blended color
+ backgroundVector = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, alpha);
TColor packed = default(TColor);
- packed.PackFromVector4(color);
+ packed.PackFromVector4(backgroundVector);
sourcePixels[x, y] = packed;
}
});
diff --git a/src/ImageSharp/Filters/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Filters/Processors/Overlays/GlowProcessor.cs
index 496a3297d0..6b1fe40c2c 100644
--- a/src/ImageSharp/Filters/Processors/Overlays/GlowProcessor.cs
+++ b/src/ImageSharp/Filters/Processors/Overlays/GlowProcessor.cs
@@ -77,15 +77,11 @@ namespace ImageSharp.Processors
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
- if (ellipse.Contains(offsetX, offsetY))
- {
- // TODO: Premultiply?
- float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
- Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
- TColor packed = default(TColor);
- packed.PackFromVector4(Vector4.Lerp(glowColor.ToVector4(), sourceColor, distance / maxDistance));
- sourcePixels[offsetX, offsetY] = packed;
- }
+ float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
+ Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
+ TColor packed = default(TColor);
+ packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance))));
+ sourcePixels[offsetX, offsetY] = packed;
}
});
}
diff --git a/src/ImageSharp/Filters/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Filters/Processors/Overlays/VignetteProcessor.cs
index 85c4af51a7..cdc8019140 100644
--- a/src/ImageSharp/Filters/Processors/Overlays/VignetteProcessor.cs
+++ b/src/ImageSharp/Filters/Processors/Overlays/VignetteProcessor.cs
@@ -86,7 +86,7 @@ namespace ImageSharp.Processors
float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
TColor packed = default(TColor);
- packed.PackFromVector4(Vector4.Lerp(vignetteColor.ToVector4(), sourceColor, 1 - (.9F * (distance / maxDistance))));
+ packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, vignetteColor.ToVector4(), .9F * (distance / maxDistance)));
sourcePixels[offsetX, offsetY] = packed;
}
});