diff --git a/src/ImageProcessor.Playground/Program.cs b/src/ImageProcessor.Playground/Program.cs index 9cf8c673fd..b04275ff10 100644 --- a/src/ImageProcessor.Playground/Program.cs +++ b/src/ImageProcessor.Playground/Program.cs @@ -49,8 +49,8 @@ namespace ImageProcessor.PlayGround di.Create(); } - Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask.png")); - IEnumerable files = GetFilesByExtensions(di, ".png"); + Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask2.png")); + IEnumerable files = GetFilesByExtensions(di, ".jpg"); //IEnumerable files = GetFilesByExtensions(di, ".gif", ".webp", ".bmp", ".jpg", ".png", ".tif"); foreach (FileInfo fileInfo in files) @@ -66,7 +66,7 @@ namespace ImageProcessor.PlayGround { using (ImageFactory imageFactory = new ImageFactory(true)) { - Size size = new Size(400, 0); + Size size = new Size(800, 0); ResizeLayer layer = new ResizeLayer(size, ResizeMode.Max, AnchorPosition.Center, false); //ContentAwareResizeLayer layer = new ContentAwareResizeLayer(size) @@ -78,16 +78,17 @@ namespace ImageProcessor.PlayGround //.BackgroundColor(Color.White) //.Resize(new Size((int)(size.Width * 1.1), 0)) //.ContentAwareResize(layer) - //.Constrain(size) - //.Mask(mask) + .Constrain(size) + .Mask(mask) + //.Format(new PngFormat()) //.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() + //.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/mask2.png b/src/ImageProcessor.Playground/images/input/mask2.png new file mode 100644 index 0000000000..3b8433202c Binary files /dev/null and b/src/ImageProcessor.Playground/images/input/mask2.png differ diff --git a/src/ImageProcessor.Playground/images/input/mask3.png b/src/ImageProcessor.Playground/images/input/mask3.png new file mode 100644 index 0000000000..582348f7b1 Binary files /dev/null and b/src/ImageProcessor.Playground/images/input/mask3.png differ diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs index 2eb0e22fcb..ae92ef6ad6 100644 --- a/src/ImageProcessor/ImageFactory.cs +++ b/src/ImageProcessor/ImageFactory.cs @@ -671,20 +671,23 @@ namespace ImageProcessor } /// - /// 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. + /// Applies the given image mask to the current image. /// /// /// The image containing the mask to apply. /// + /// + /// The to place the mask if it not the same dimensions as the original image. + /// If no position is set, the mask will be centered within the image. + /// /// /// The current instance of the class. /// - public ImageFactory Mask(Image imageMask) + public ImageFactory Mask(Image imageMask, Point? point = null) { if (this.ShouldProcess) { - Mask mask = new Mask { DynamicParameter = imageMask }; + Mask mask = new Mask { DynamicParameter = new Tuple(imageMask, point) }; 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 1ad4f9b3d2..f6cefeb3d9 100644 --- a/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs +++ b/src/ImageProcessor/Imaging/Filters/Photo/ComicMatrixFilter.cs @@ -92,12 +92,12 @@ namespace ImageProcessor.Imaging.Filters.Photo // Create the pattern mask. using (Graphics graphics = Graphics.FromImage(patternBitmap)) { - graphics.Clear(Color.Black); + graphics.Clear(Color.Transparent); graphics.SmoothingMode = SmoothingMode.HighQuality; - for (int y = 0; y < image.Height; y += 8) + for (int y = 0; y < height; y += 8) { - for (int x = 0; x < image.Width; x += 4) + for (int x = 0; x < width; x += 4) { graphics.FillEllipse(Brushes.White, x, y, 3, 3); graphics.FillEllipse(Brushes.White, x + 2, y + 4, 3, 3); diff --git a/src/ImageProcessor/Imaging/Helpers/Effects.cs b/src/ImageProcessor/Imaging/Helpers/Effects.cs index e079f7e37d..7cef4ebea0 100644 --- a/src/ImageProcessor/Imaging/Helpers/Effects.cs +++ b/src/ImageProcessor/Imaging/Helpers/Effects.cs @@ -155,7 +155,7 @@ namespace ImageProcessor.Imaging.Helpers if (sourceColor.A != 0) { - sourceBitmap.SetPixel(x, y, Color.FromArgb(maskColor.B, sourceColor.R, sourceColor.G, sourceColor.B)); + sourceBitmap.SetPixel(x, y, Color.FromArgb(maskColor.A, sourceColor.R, sourceColor.G, sourceColor.B)); } // ReSharper restore AccessToDisposedClosure diff --git a/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs b/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs index 2e552f35c2..a9e997d178 100644 --- a/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs +++ b/src/ImageProcessor/Imaging/Helpers/ImageMaths.cs @@ -145,10 +145,10 @@ namespace ImageProcessor.Imaging.Helpers using (FastBitmap fastBitmap = new FastBitmap(bitmap)) { - topLeft.Y = getMinY(fastBitmap) + 1; - topLeft.X = getMinX(fastBitmap) + 1; - bottomRight.Y = getMaxY(fastBitmap); - bottomRight.X = getMaxX(fastBitmap); + topLeft.Y = getMinY(fastBitmap); + topLeft.X = getMinX(fastBitmap); + bottomRight.Y = getMaxY(fastBitmap) + 1; + bottomRight.X = getMaxX(fastBitmap) + 1; } return ImageMaths.GetBoundingRectangle(topLeft, bottomRight); diff --git a/src/ImageProcessor/Processors/Mask.cs b/src/ImageProcessor/Processors/Mask.cs index 7a8aee4635..44b61bc8f6 100644 --- a/src/ImageProcessor/Processors/Mask.cs +++ b/src/ImageProcessor/Processors/Mask.cs @@ -15,6 +15,7 @@ namespace ImageProcessor.Processors using System.Drawing; using ImageProcessor.Common.Exceptions; + using ImageProcessor.Imaging.Colors; using ImageProcessor.Imaging.Helpers; /// @@ -63,30 +64,67 @@ namespace ImageProcessor.Processors { Bitmap newImage = null; Bitmap mask = null; - Bitmap maskResized = null; + Bitmap maskCropped = null; + Bitmap maskPositioned = null; Image image = factory.Image; try { 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); - - // 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)) + Tuple parameters = this.DynamicParameter; + mask = new Bitmap(parameters.Item1); + Point? position = parameters.Item2.HasValue ? parameters.Item2 : null; + + if (mask.Size != image.Size) { - graphics.Clear(Color.Transparent); - graphics.DrawImage(mask, new PointF(centered.X, centered.Y)); - } + Rectangle parent = new Rectangle(0, 0, width, height); + Rectangle child = ImageMaths.GetFilteredBoundingRectangle(mask, 0, RgbaComponent.A); + maskCropped = new Bitmap(child.Width, child.Height); + + // First crop any bounding transparency. + using (Graphics graphics = Graphics.FromImage(maskCropped)) + { + graphics.Clear(Color.Transparent); + graphics.DrawImage( + mask, + new Rectangle(0, 0, child.Width, child.Height), + child.X, + child.Y, + child.Width, + child.Height, + GraphicsUnit.Pixel); + } - newImage = Effects.ApplyMask(image, maskResized); + // Now position the mask in an image of the same dimensions as the original. + maskPositioned = new Bitmap(width, height); + using (Graphics graphics = Graphics.FromImage(maskPositioned)) + { + graphics.Clear(Color.Transparent); + + if (position != null) + { + // Apply the mask at the given position. + graphics.DrawImage(maskCropped, position.Value); + } + else + { + // Center it instead + RectangleF centered = ImageMaths.CenteredRectangle(parent, child); + graphics.DrawImage(maskCropped, new PointF(centered.X, centered.Y)); + } + } + + newImage = Effects.ApplyMask(image, maskPositioned); + maskCropped.Dispose(); + maskPositioned.Dispose(); + } + else + { + newImage = Effects.ApplyMask(image, mask); + mask.Dispose(); + } - mask.Dispose(); - maskResized.Dispose(); image.Dispose(); image = newImage; } @@ -97,9 +135,14 @@ namespace ImageProcessor.Processors mask.Dispose(); } - if (maskResized != null) + if (maskCropped != null) + { + maskCropped.Dispose(); + } + + if (maskPositioned != null) { - maskResized.Dispose(); + maskPositioned.Dispose(); } if (newImage != null)