From 7bab72aeca361c12f0e949a9a8e4c16f732f2bfc Mon Sep 17 00:00:00 2001 From: James South Date: Sat, 11 Oct 2014 13:02:50 +0100 Subject: [PATCH] Mask now accepts position instructions Former-commit-id: 8e40db9486bd08d8dcf2850604d3e5f40dfeaee1 Former-commit-id: 803d99ed0ff547a62870e458ef95e918f31cf93f --- src/ImageProcessor.Playground/Program.cs | 15 ++-- .../images/input/mask2.png | Bin 0 -> 3620 bytes .../images/input/mask3.png | Bin 0 -> 2869 bytes src/ImageProcessor/ImageFactory.cs | 11 ++- .../Filters/Photo/ComicMatrixFilter.cs | 6 +- src/ImageProcessor/Imaging/Helpers/Effects.cs | 2 +- .../Imaging/Helpers/ImageMaths.cs | 8 +- src/ImageProcessor/Processors/Mask.cs | 77 ++++++++++++++---- 8 files changed, 83 insertions(+), 36 deletions(-) create mode 100644 src/ImageProcessor.Playground/images/input/mask2.png create mode 100644 src/ImageProcessor.Playground/images/input/mask3.png 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 0000000000000000000000000000000000000000..3b8433202c9424678b19a7b7559e23fce7d20bd0 GIT binary patch literal 3620 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 D=Lz`h literal 0 HcmV?d00001 diff --git a/src/ImageProcessor.Playground/images/input/mask3.png b/src/ImageProcessor.Playground/images/input/mask3.png new file mode 100644 index 0000000000000000000000000000000000000000..582348f7b1d6a8ba87fede9d0fb9d04cb5f980b8 GIT binary patch literal 2869 zcmeH}eNa@_8OEQ>!XlVe7{S3SMqn==f&v*YmN8L50q7I(WN0wOlxdx&uN%jylbQCf z&iF@nX6MX#_Pl4`bI<$!p8I_1@}-VQF%p1-DFtr8HWv_nKfzAQ)zNQP(h#~ae_1|o zym{iSsxX>I)F`XA1C!pn{S{)2QZg{F z_1rOeEl{1L=d0fW<>jKrHwox#lmBj(*-$B{*V(C11WbOS<*A9#o;UMTM;-*;x1v*r z9|K}q&$XMhL5yKpEHKUWei|8y)J(UfF z;7XP%vz}?dyVh~3`zheFVkhJA0;)4~C)*x|zN7L1>BDIV%;Rx}yc4)k>{PfYV0MN+ zlWnF2ro3BvBNl;e{JO7y11(V2F6Ea6lw|4gY*#3<2jm)QBpQK!e!|yJ=lz&<74es8 zL9#<+|F9!_zr0QwnSs2g)!!=okH816p+e)d9i?tD(-_NvwvGSHXT~6JmfEMdBhh(C zepDJR1Cq^SN$l$o#HBl8*!(F7WUCztZ>76c&K4MNhT`HHG1(Ynz#1z*iM>L?GSO+& zrb5f)Q9kow0gq(rQEU@s*Tvy8r;?DOPiC9wz)n8uH4l@J-IbvEBf4?~o7 zHG#E;po!ObO_zWIHCJ(4z%S%@shcphLY!oHW5n z=rrXgq~XoLdh13KTVz98mLA7;h2QP*fXv%`{TuH@3w zGpYK6_i*NyeB9Sh2n&Mfb0U>fPh{KecSuDbdW+{8^3v~7<{kmY=jh2S$99KxvA~~| zA?DVJr%pYM4bcz#WK+^3pdI9|8G~d6L3}PGe6AE%1-V+pKQ+cm0Qqpy5%Vnpqh9&6 zG(ZuJR&>g61(}H7{-U{=*sX{ggY2$UP4*0##(wim%5WS|Xx&H&!v086NB=IML~_hm z-v-(|;!d7Pfxni&F)xu@B&wsVm*D>%hkwIlXv@rp(mo`cn(jJVdfRTub$VzTZ+?_k zHW4`8YB%NiK<*retDEpVdR)#9y4MDN#s7FX(9CR}lSKZY&5>)xr8qppYk*Rh_y&K4 z?Cu?T-N05L+mg2oJO}h0lIsTOM1B48-W<~VEcu19MP#>GYVPLUKp;%@^!^0Mv#Hg+ z&(avK9{P&(yhOF1@@aF6f9s{ctxh<)=S%YPj>F3fFWgrz8~;z{K6(P@pSv|a^ZKW& UvFPU2^4oc_!sP{TF0S1BFYi^? - /// 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)