diff --git a/src/ImageProcessor.Playground/Program.cs b/src/ImageProcessor.Playground/Program.cs
index ec95d6f19..13dbc37f8 100644
--- a/src/ImageProcessor.Playground/Program.cs
+++ b/src/ImageProcessor.Playground/Program.cs
@@ -18,15 +18,19 @@ namespace ImageProcessor.PlayGround
using System.Linq;
using ImageProcessor;
+ using ImageProcessor.Configuration;
using ImageProcessor.Imaging;
using ImageProcessor.Imaging.Filters.EdgeDetection;
using ImageProcessor.Imaging.Filters.Photo;
+ using ImageProcessor.Imaging.Formats;
///
/// The program.
///
public class Program
{
+ protected static readonly IEnumerable formats = ImageProcessorBootstrapper.Instance.SupportedImageFormats;
+
///
/// The main routine.
///
@@ -45,6 +49,7 @@ namespace ImageProcessor.PlayGround
di.Create();
}
+ Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask.png"));
IEnumerable files = GetFilesByExtensions(di, ".jpg");
//IEnumerable files = GetFilesByExtensions(di, ".gif", ".webp", ".bmp", ".jpg", ".png", ".tif");
@@ -73,15 +78,16 @@ namespace ImageProcessor.PlayGround
//.BackgroundColor(Color.White)
//.Resize(new Size((int)(size.Width * 1.1), 0))
//.ContentAwareResize(layer)
- .Constrain(size)
- .Mask()
+ //.Constrain(size)
+ //.Mask(mask)
+ //.BackgroundColor(Color.HotPink)
//.ReplaceColor(Color.FromArgb(255, 1, 107, 165), Color.FromArgb(255, 1, 165, 13), 80)
//.Resize(layer)
//.DetectEdges(new SobelEdgeFilter(), false)
//.DetectEdges(new LaplacianOfGaussianEdgeFilter())
//.EntropyCrop()
//.Filter(MatrixFilters.Invert)
- //.Filter(MatrixFilters.Comic)
+ .Filter(MatrixFilters.Comic)
//.Filter(MatrixFilters.HiSatch)
//.Pixelate(8)
//.GaussianSharpen(10)
diff --git a/src/ImageProcessor.Playground/images/input/mask.png b/src/ImageProcessor.Playground/images/input/mask.png
new file mode 100644
index 000000000..f86272dc3
Binary files /dev/null and b/src/ImageProcessor.Playground/images/input/mask.png differ
diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs
index 4137c4f30..2eb0e22fc 100644
--- a/src/ImageProcessor/ImageFactory.cs
+++ b/src/ImageProcessor/ImageFactory.cs
@@ -670,11 +670,21 @@ namespace ImageProcessor
return this;
}
- public ImageFactory Mask()
+ ///
+ /// Applies the given image mask to the current image. If the mask is not the same size as the image
+ /// it will be centered against the image.
+ ///
+ ///
+ /// The image containing the mask to apply.
+ ///
+ ///
+ /// The current instance of the class.
+ ///
+ public ImageFactory Mask(Image imageMask)
{
if (this.ShouldProcess)
{
- Mask mask = new Mask();
+ Mask mask = new Mask { DynamicParameter = imageMask };
this.CurrentImageFormat.ApplyProcessor(mask.ProcessImage, this);
}
diff --git a/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs b/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs
index 4dc980ce0..1ad4f9b3d 100644
--- a/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs
+++ b/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs
@@ -10,7 +10,6 @@
namespace ImageProcessor.Imaging.Filters.Photo
{
- using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
@@ -106,8 +105,8 @@ namespace ImageProcessor.Imaging.Filters.Photo
}
}
- // Transfer the alpha channel from the mask to the high saturation image.
- ApplyMask(patternBitmap, lowBitmap);
+ // Transfer the alpha channel from the mask to the low saturation image.
+ lowBitmap = Effects.ApplyMask(lowBitmap, patternBitmap);
using (Graphics graphics = Graphics.FromImage(newImage))
{
@@ -195,8 +194,8 @@ namespace ImageProcessor.Imaging.Filters.Photo
using (Bitmap temp = filter.Process2DFilter(source))
{
destination = new InvertMatrixFilter().TransformImage(temp, destination);
-
- // Darken it slightly
+
+ // Darken it slightly to aid detection
destination = Adjustments.Brightness(destination, -5);
}
@@ -222,56 +221,9 @@ namespace ImageProcessor.Imaging.Filters.Photo
});
}
+ // Darken it again to average out the color.
+ destination = Adjustments.Brightness(destination, -5);
return (Bitmap)destination;
}
-
- ///
- /// Applies a mask .
- ///
- ///
- /// The source.
- ///
- ///
- /// The destination.
- ///
- ///
- /// Thrown if the two images are of different size.
- ///
- private static void ApplyMask(Image source, Image destination)
- {
- if (source.Size != destination.Size)
- {
- throw new ArgumentException();
- }
-
- using (FastBitmap sourceBitmap = new FastBitmap(source))
- {
- using (FastBitmap destinationBitmap = new FastBitmap(destination))
- {
- int width = source.Width;
- int height = source.Height;
-
- Parallel.For(
- 0,
- height,
- y =>
- {
- for (int x = 0; x < width; x++)
- {
- // ReSharper disable AccessToDisposedClosure
- Color sourceColor = sourceBitmap.GetPixel(x, y);
- Color destinationColor = destinationBitmap.GetPixel(x, y);
-
- if (destinationColor.A != 0)
- {
- destinationBitmap.SetPixel(x, y, Color.FromArgb(sourceColor.B, destinationColor.R, destinationColor.G, destinationColor.B));
- }
-
- // ReSharper restore AccessToDisposedClosure
- }
- });
- }
- }
- }
}
}
\ No newline at end of file
diff --git a/src/ImageProcessor/Imaging/Helpers/Effects.cs b/src/ImageProcessor/Imaging/Helpers/Effects.cs
index 3af918a21..e079f7e37 100644
--- a/src/ImageProcessor/Imaging/Helpers/Effects.cs
+++ b/src/ImageProcessor/Imaging/Helpers/Effects.cs
@@ -13,6 +13,7 @@ namespace ImageProcessor.Imaging.Helpers
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
+ using System.Threading.Tasks;
///
/// Provides reusable effect methods to apply to images.
@@ -69,8 +70,8 @@ namespace ImageProcessor.Imaging.Helpers
}
else
{
- centerColor = Color.FromArgb(0, baseColor.R, baseColor.G, baseColor.B);
- edgeColor = Color.FromArgb(255, baseColor.R, baseColor.G, baseColor.B);
+ centerColor = Color.FromArgb(0, baseColor.R, baseColor.G, baseColor.B);
+ edgeColor = Color.FromArgb(255, baseColor.R, baseColor.G, baseColor.B);
}
brush.WrapMode = WrapMode.Tile;
@@ -108,5 +109,71 @@ namespace ImageProcessor.Imaging.Helpers
{
return Vignette(source, baseColor, rectangle, true);
}
+
+ ///
+ /// Applies the given image mask to the source.
+ ///
+ ///
+ /// The source .
+ ///
+ ///
+ /// The mask .
+ ///
+ ///
+ /// Thrown if the two images are of different size.
+ ///
+ ///
+ /// The masked .
+ ///
+ public static Bitmap ApplyMask(Image source, Image mask)
+ {
+ if (mask.Size != source.Size)
+ {
+ throw new ArgumentException();
+ }
+
+ int width = mask.Width;
+ int height = mask.Height;
+
+ Bitmap toMask = new Bitmap(source);
+
+ // Loop through and replace the alpha channel
+ using (FastBitmap maskBitmap = new FastBitmap(mask))
+ {
+ using (FastBitmap sourceBitmap = new FastBitmap(toMask))
+ {
+ Parallel.For(
+ 0,
+ height,
+ y =>
+ {
+ for (int x = 0; x < width; x++)
+ {
+ // ReSharper disable AccessToDisposedClosure
+ Color maskColor = maskBitmap.GetPixel(x, y);
+ Color sourceColor = sourceBitmap.GetPixel(x, y);
+
+ if (sourceColor.A != 0)
+ {
+ sourceBitmap.SetPixel(x, y, Color.FromArgb(maskColor.B, sourceColor.R, sourceColor.G, sourceColor.B));
+ }
+
+ // ReSharper restore AccessToDisposedClosure
+ }
+ });
+ }
+ }
+
+ // Ensure the background is cleared out on non alpha supporting formats.
+ Bitmap clear = new Bitmap(width, height);
+ using (Graphics graphics = Graphics.FromImage(clear))
+ {
+ graphics.Clear(Color.Transparent);
+ graphics.DrawImage(toMask, 0, 0, width, height);
+ }
+
+ toMask.Dispose();
+ return clear;
+ }
}
}
diff --git a/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs b/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs
index 1d2ac60b0..c801cb5e3 100644
--- a/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs
+++ b/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs
@@ -15,7 +15,7 @@ namespace ImageProcessor.Imaging.Helpers
///
/// Provides reusable mathematical methods to apply to images.
///
- public class ImageMaths
+ public static class ImageMaths
{
///
/// Gets the bounding from the given points.
@@ -29,13 +29,13 @@ namespace ImageProcessor.Imaging.Helpers
///
/// The bounding .
///
- public Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight)
+ public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight)
{
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
}
///
- /// Gets a centered within it's parent.
+ /// Gets a representing the child centered relative to the parent.
///
///
/// The parent .
@@ -46,17 +46,33 @@ namespace ImageProcessor.Imaging.Helpers
///
/// The centered .
///
- public Rectangle CenteredRectangle(Rectangle parent, Rectangle child)
+ public static RectangleF CenteredRectangle(Rectangle parent, Rectangle child)
{
- if (parent.Size.Width < child.Size.Width && parent.Size.Height < child.Size.Height)
- {
- return parent;
- }
-
- int x = (parent.Width - child.Width) / 2;
- int y = (parent.Height - child.Height) / 2;
+ float x = (parent.Width - child.Width) / 2.0F;
+ float y = (parent.Height - child.Height) / 2.0F;
+ int width = child.Width;
+ int height = child.Height;
+ return new RectangleF(x, y, width, height);
+ }
- return new Rectangle(x, y, child.Width, child.Height);
+ ///
+ /// Returns the array of matching the bounds of the given rectangle.
+ ///
+ ///
+ /// The to return the points from.
+ ///
+ ///
+ /// The array.
+ ///
+ public static Point[] ToPoints(Rectangle rectangle)
+ {
+ return new[]
+ {
+ new Point(rectangle.Left, rectangle.Top),
+ new Point(rectangle.Right, rectangle.Top),
+ new Point(rectangle.Right, rectangle.Bottom),
+ new Point(rectangle.Left, rectangle.Bottom)
+ };
}
}
}
diff --git a/src/ImageProcessor/Processors/BackgroundColor.cs b/src/ImageProcessor/Processors/BackgroundColor.cs
index a41396f90..c00ce6d3e 100644
--- a/src/ImageProcessor/Processors/BackgroundColor.cs
+++ b/src/ImageProcessor/Processors/BackgroundColor.cs
@@ -55,8 +55,11 @@ namespace ImageProcessor.Processors
try
{
+ int width = image.Width;
+ int height = image.Height;
+
Color backgroundColor = this.DynamicParameter;
- newImage = new Bitmap(image.Width, image.Height);
+ newImage = new Bitmap(width, height);
// Make a graphics object from the empty bitmap.
using (Graphics graphics = Graphics.FromImage(newImage))
@@ -65,7 +68,7 @@ namespace ImageProcessor.Processors
graphics.Clear(backgroundColor);
// Draw passed in image onto graphics object.
- graphics.DrawImage(image, 0, 0);
+ graphics.DrawImage(image, 0, 0, width, height);
}
image.Dispose();
diff --git a/src/ImageProcessor/Processors/Filter.cs b/src/ImageProcessor/Processors/Filter.cs
index 69ed65a73..a9197ce83 100644
--- a/src/ImageProcessor/Processors/Filter.cs
+++ b/src/ImageProcessor/Processors/Filter.cs
@@ -13,7 +13,6 @@ namespace ImageProcessor.Processors
using System;
using System.Collections.Generic;
using System.Drawing;
- using System.Drawing.Imaging;
using ImageProcessor.Common.Exceptions;
using ImageProcessor.Imaging.Filters.Photo;
diff --git a/src/ImageProcessor/Processors/Mask.cs b/src/ImageProcessor/Processors/Mask.cs
index 372be395e..7a8aee463 100644
--- a/src/ImageProcessor/Processors/Mask.cs
+++ b/src/ImageProcessor/Processors/Mask.cs
@@ -15,9 +15,11 @@ namespace ImageProcessor.Processors
using System.Drawing;
using ImageProcessor.Common.Exceptions;
+ using ImageProcessor.Imaging.Helpers;
///
- /// Applies a mask to the given image.
+ /// Applies a mask to the given image. If the mask is not the same size as the image
+ /// it will be centered against the image.
///
public class Mask : IGraphicsProcessor
{
@@ -60,29 +62,46 @@ namespace ImageProcessor.Processors
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
+ Bitmap mask = null;
+ Bitmap maskResized = null;
Image image = factory.Image;
- Size original = image.Size;
- Size smaller = new Size(image.Width / 2, image.Height / 2);
- int x = (original.Width - smaller.Width) / 2;
- int y = (original.Height - smaller.Height) / 2;
-
- int width = image.Width;
- int height = image.Height;
try
{
- newImage = new Bitmap(original.Width, image.Height);
+ int width = image.Width;
+ int height = image.Height;
+ mask = new Bitmap(this.DynamicParameter);
+ Rectangle parent = new Rectangle(0, 0, width, height);
+ Rectangle child = new Rectangle(0, 0, mask.Width, mask.Height);
+ RectangleF centered = ImageMaths.CenteredRectangle(parent, child);
- using (Graphics graphics = Graphics.FromImage(newImage))
+ // Resize the mask to the size of the input image so that we can apply it.
+ maskResized = new Bitmap(width, height);
+ using (Graphics graphics = Graphics.FromImage(maskResized))
{
- graphics.DrawImage(image, x, y, smaller.Width, smaller.Height);
+ graphics.Clear(Color.Transparent);
+ graphics.DrawImage(mask, new PointF(centered.X, centered.Y));
}
+ newImage = Effects.ApplyMask(image, maskResized);
+
+ mask.Dispose();
+ maskResized.Dispose();
image.Dispose();
image = newImage;
}
catch (Exception ex)
{
+ if (mask != null)
+ {
+ mask.Dispose();
+ }
+
+ if (maskResized != null)
+ {
+ maskResized.Dispose();
+ }
+
if (newImage != null)
{
newImage.Dispose();