diff --git a/Settings.StyleCop b/Settings.StyleCop
index b3cd5ee7e..dbff3379a 100644
--- a/Settings.StyleCop
+++ b/Settings.StyleCop
@@ -35,6 +35,7 @@
Paeth
th
desensitivity
+ premultiplied
diff --git a/src/ImageSharp/Colors/Vector4BlendTransforms.cs b/src/ImageSharp/Colors/Vector4BlendTransforms.cs
index 4851baedf..ca8ac55c0 100644
--- a/src/ImageSharp/Colors/Vector4BlendTransforms.cs
+++ b/src/ImageSharp/Colors/Vector4BlendTransforms.cs
@@ -185,6 +185,28 @@ namespace ImageSharp
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 by their W component 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);
+ 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);
+ }
+
///
/// Multiplies or screens the color component, depending on the component value.
///
diff --git a/src/ImageSharp/Filters/Overlays/Blend.cs b/src/ImageSharp/Filters/Overlays/Blend.cs
index 448fb7aa0..4a1fc534d 100644
--- a/src/ImageSharp/Filters/Overlays/Blend.cs
+++ b/src/ImageSharp/Filters/Overlays/Blend.cs
@@ -21,11 +21,11 @@ namespace ImageSharp
/// 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, ImageBase image, int percent = 50)
+ public static Image Blend(this Image source, Image image, int percent = 50)
where TColor : struct, IPackedPixel
where TPacked : struct
{
- return Blend(source, image, percent, source.Bounds);
+ return Blend(source, image, percent, default(Size), default(Point));
}
///
@@ -36,15 +36,24 @@ namespace ImageSharp
/// The pixel format.
/// The packed format. uint, long, float.
/// The opacity of the image image to blend. Must be between 0 and 100.
- ///
- /// The structure that specifies the portion of the image object to alter.
- ///
+ /// The size to draw the blended image.
+ /// The location to draw the blended image.
/// The .
- public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle)
+ public static Image Blend(this Image source, Image image, int percent, Size size, Point location)
where TColor : struct, IPackedPixel
where TPacked : struct
{
- return source.Process(rectangle, new BlendProcessor(image, percent));
+ if (size == default(Size))
+ {
+ size = new Size(image.Width, image.Height);
+ }
+
+ if (location == default(Point))
+ {
+ location = Point.Empty;
+ }
+
+ return source.Process(source.Bounds, new BlendProcessor(image, size, location, percent));
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Filters/Processors/Overlays/BlendProcessor.cs b/src/ImageSharp/Filters/Processors/Overlays/BlendProcessor.cs
index 84e49f596..3592c7921 100644
--- a/src/ImageSharp/Filters/Processors/Overlays/BlendProcessor.cs
+++ b/src/ImageSharp/Filters/Processors/Overlays/BlendProcessor.cs
@@ -18,58 +18,60 @@ namespace ImageSharp.Processors
where TColor : struct, IPackedPixel
where TPacked : struct
{
- ///
- /// The image to blend.
- ///
- private readonly ImageBase blend;
-
///
/// Initializes a new instance of the class.
///
- ///
- /// The image to blend with the currently processing image.
- /// Disposal of this image is the responsibility of the developer.
- ///
+ /// 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 BlendProcessor(ImageBase image, int alpha = 100)
+ public BlendProcessor(Image image, Size size, Point location, int alpha = 100)
{
Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha));
- this.blend = image;
- this.Value = alpha;
+ this.Image = image;
+ this.Size = size;
+ this.Alpha = alpha;
+ this.Location = location;
}
+ ///
+ /// Gets the image to blend.
+ ///
+ public Image Image { get; private set; }
+
///
/// Gets the alpha percentage value.
///
- public int Value { get; }
+ public int Alpha { get; }
+
+ ///
+ /// Gets the size to draw the blended image.
+ ///
+ public Size Size { get; }
+
+ ///
+ /// Gets the location to draw the blended image.
+ ///
+ public Point Location { get; }
///
protected override void Apply(ImageBase source, Rectangle sourceRectangle, int startY, int endY)
{
- int startX = sourceRectangle.X;
- int endX = sourceRectangle.Right;
- Rectangle bounds = this.blend.Bounds;
-
- // Align start/end positions.
- int minX = Math.Max(0, startX);
- int maxX = Math.Min(source.Width, endX);
- int minY = Math.Max(0, startY);
- int maxY = Math.Min(source.Height, endY);
-
- // Reset offset if necessary.
- if (minX > 0)
+ if (this.Image.Bounds.Size != this.Size)
{
- startX = 0;
+ this.Image = this.Image.Resize(this.Size.Width, this.Size.Height);
}
- if (minY > 0)
- {
- startY = 0;
- }
+ // 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, startY);
+ int maxY = Math.Min(this.Location.Y + bounds.Height, endY);
- float alpha = this.Value / 100F;
+ float alpha = this.Alpha / 100F;
- using (PixelAccessor toBlendPixels = this.blend.Lock())
+ using (PixelAccessor toBlendPixels = this.Image.Lock())
using (PixelAccessor sourcePixels = source.Lock())
{
Parallel.For(
@@ -78,26 +80,20 @@ namespace ImageSharp.Processors
this.ParallelOptions,
y =>
{
- int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
- int offsetX = x - startX;
- Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
+ Vector4 color = sourcePixels[x, y].ToVector4();
+ Vector4 blendedColor = toBlendPixels[x - minX, y - minY].ToVector4();
- if (bounds.Contains(offsetX, offsetY))
+ if (blendedColor.W > 0)
{
- Vector4 blendedColor = toBlendPixels[offsetX, offsetY].ToVector4();
-
- if (blendedColor.W > 0)
- {
- // Lerping colors is dependent on the alpha of the blended color
- color = Vector4.Lerp(color, blendedColor, alpha > 0 ? alpha : blendedColor.W);
- }
+ // Lerping colors is dependent on the alpha of the blended color
+ color = Vector4BlendTransforms.PremultipliedLerp(color, blendedColor, alpha);
}
TColor packed = default(TColor);
packed.PackFromVector4(color);
- sourcePixels[offsetX, offsetY] = packed;
+ sourcePixels[x, y] = packed;
}
});
}
diff --git a/src/ImageSharp/Numerics/Rectangle.cs b/src/ImageSharp/Numerics/Rectangle.cs
index be457fc5a..3f6267a46 100644
--- a/src/ImageSharp/Numerics/Rectangle.cs
+++ b/src/ImageSharp/Numerics/Rectangle.cs
@@ -127,6 +127,11 @@ namespace ImageSharp
}
}
+ ///
+ /// Gets the size of this .
+ ///
+ public Size Size => new Size(this.Width, this.Height);
+
///
/// Gets a value indicating whether this is empty.
///
diff --git a/tests/ImageSharp.Tests/Processors/Filters/BlendTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BlendTest.cs
index 653524e20..8c3bf932f 100644
--- a/tests/ImageSharp.Tests/Processors/Filters/BlendTest.cs
+++ b/tests/ImageSharp.Tests/Processors/Filters/BlendTest.cs
@@ -6,6 +6,7 @@
namespace ImageSharp.Tests
{
using System.IO;
+ using System.Linq;
using Xunit;
@@ -16,7 +17,9 @@ namespace ImageSharp.Tests
{
string path = CreateOutputDirectory("Blend");
- Image blend;
+ Image blend;// = new Image(400, 400);
+ // blend.BackgroundColor(Color.RebeccaPurple);
+
using (FileStream stream = File.OpenRead(TestImages.Bmp.Car))
{
blend = new Image(stream);
@@ -28,8 +31,8 @@ namespace ImageSharp.Tests
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{
- image.Blend(blend)
- .Save(output);
+ image.Blend(blend, 75, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4))
+ .Save(output);
}
}
}