diff --git a/src/ImageProcessorCore/Filters/Blend.cs b/src/ImageProcessorCore/Filters/Blend.cs
new file mode 100644
index 000000000..96f8f60cd
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Blend.cs
@@ -0,0 +1,62 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+// -------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessorCore
+{
+ using Processors;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Combines 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 packed format. long, float.
+ /// The opacity of the image image to blend. Must be between 0 and 100.
+ /// A delegate which is called as progress is made processing the image.
+ /// The .
+ public static Image Blend(this Image source, ImageBase image, int percent = 50, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ return Blend(source, image, percent, source.Bounds, progressHandler);
+ }
+
+ ///
+ /// Combines 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 packed format. 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.
+ ///
+ /// A delegate which is called as progress is made processing the image.
+ /// The .
+ public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null)
+ where T : IPackedVector
+ where TP : struct
+ {
+ BlendProcessor processor = new BlendProcessor(image, percent);
+ processor.OnProgress += progressHandler;
+
+ try
+ {
+ return source.Process(rectangle, processor);
+ }
+ finally
+ {
+ processor.OnProgress -= progressHandler;
+ }
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs
new file mode 100644
index 000000000..56c754729
--- /dev/null
+++ b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs
@@ -0,0 +1,93 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+ using System.Threading.Tasks;
+
+ ///
+ /// Combines two images together by blending the pixels.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ public class BlendProcessor : ImageProcessor
+ where T : IPackedVector
+ where TP : 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 opacity of the image to blend. Between 0 and 100.
+ public BlendProcessor(ImageBase image, int alpha = 100)
+ {
+ Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha));
+ this.blend = image;
+ this.Value = alpha;
+ }
+
+ ///
+ /// Gets the alpha percentage value.
+ ///
+ public int Value { get; }
+
+ ///
+ protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
+ {
+ int sourceY = sourceRectangle.Y;
+ int sourceBottom = sourceRectangle.Bottom;
+ int startX = sourceRectangle.X;
+ int endX = sourceRectangle.Right;
+ Rectangle bounds = this.blend.Bounds;
+ float alpha = this.Value / 100f;
+
+ using (IPixelAccessor toBlendPixels = this.blend.Lock())
+ using (IPixelAccessor sourcePixels = source.Lock())
+ using (IPixelAccessor targetPixels = target.Lock())
+ {
+ Parallel.For(
+ startY,
+ endY,
+ y =>
+ {
+ if (y >= sourceY && y < sourceBottom)
+ {
+ for (int x = startX; x < endX; x++)
+ {
+ Vector4 color = sourcePixels[x, y].ToVector4();
+
+ if (bounds.Contains(x, y))
+ {
+ Vector4 blendedColor = toBlendPixels[x, y].ToVector4();
+
+ if (blendedColor.W > 0)
+ {
+ // Lerping colors is dependent on the alpha of the blended color
+ float alphaFactor = alpha > 0 ? alpha : blendedColor.W;
+ color = Vector4.Lerp(color, blendedColor, alphaFactor);
+ }
+ }
+
+ T packed = default(T);
+ packed.PackVector(color);
+ targetPixels[x, y] = packed;
+ }
+
+ this.OnRowProcessed();
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/BlendTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/BlendTest.cs
new file mode 100644
index 000000000..2aa6ce044
--- /dev/null
+++ b/tests/ImageProcessorCore.Tests/Processors/Filters/BlendTest.cs
@@ -0,0 +1,44 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Tests
+{
+ using System.IO;
+
+ using Xunit;
+
+ public class BlendTest : FileTestBase
+ {
+ [Fact]
+ public void ImageShouldApplyBlendFilter()
+ {
+ const string path = "TestOutput/Blend";
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+
+ Image blend;
+ using (FileStream stream = File.OpenRead("TestImages/Formats/Bmp/Car.bmp"))
+ {
+ blend = new Image(stream);
+ }
+
+ foreach (string file in Files)
+ {
+ using (FileStream stream = File.OpenRead(file))
+ {
+ string filename = Path.GetFileName(file);
+ Image image = new Image(stream);
+ using (FileStream output = File.OpenWrite($"{path}/{filename}"))
+ {
+ image.Blend(blend)
+ .Save(output);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file