From 860dde8ceeeadb35aa409dde6f089a69877c4d30 Mon Sep 17 00:00:00 2001 From: James South Date: Thu, 6 Nov 2014 14:25:47 +0000 Subject: [PATCH] Alpha now uses FastBitmap Former-commit-id: 7c2d70dc94c67c84b48084480a776f3b2d4ec6fe Former-commit-id: 71a7b03cfb9c3e4754e1ffb4a12bd9d7e5b1eb8a --- .../images/input/001.png | 3 ++ .../Imaging/Formats/PngFormat.cs | 34 ++++++------- .../Imaging/Helpers/Adjustments.cs | 42 +++++++++------- .../Quantizers/WuQuantizer/ColorMoment.cs | 11 ++--- .../Quantizers/WuQuantizer/WuQuantizer.cs | 7 ++- .../Quantizers/WuQuantizer/WuQuantizerBase.cs | 48 +++++++++++-------- 6 files changed, 80 insertions(+), 65 deletions(-) create mode 100644 src/ImageProcessor.Playground/images/input/001.png diff --git a/src/ImageProcessor.Playground/images/input/001.png b/src/ImageProcessor.Playground/images/input/001.png new file mode 100644 index 000000000..90a27aeed --- /dev/null +++ b/src/ImageProcessor.Playground/images/input/001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa4aeb79b1c2c20697d8feb5997d2756a7d007a4f530d03148301a0e51beded2 +size 155 diff --git a/src/ImageProcessor/Imaging/Formats/PngFormat.cs b/src/ImageProcessor/Imaging/Formats/PngFormat.cs index 73d2e3146..bc27f519e 100644 --- a/src/ImageProcessor/Imaging/Formats/PngFormat.cs +++ b/src/ImageProcessor/Imaging/Formats/PngFormat.cs @@ -101,28 +101,28 @@ namespace ImageProcessor.Imaging.Formats if (this.IsIndexed) { // The Wu Quantizer expects a 32bbp image. - //if (Image.GetPixelFormatSize(image.PixelFormat) != 32) - //{ + if (Image.GetPixelFormatSize(image.PixelFormat) != 32) + { - Bitmap clone = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); - clone.SetResolution(image.HorizontalResolution, image.VerticalResolution); + Bitmap clone = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); + clone.SetResolution(image.HorizontalResolution, image.VerticalResolution); - using (Graphics graphics = Graphics.FromImage(clone)) - { - graphics.Clear(Color.Transparent); - graphics.DrawImage(image, new Rectangle(0, 0, clone.Width, clone.Height)); - } + using (Graphics graphics = Graphics.FromImage(clone)) + { + graphics.Clear(Color.Transparent); + graphics.DrawImage(image, new Rectangle(0, 0, clone.Width, clone.Height)); + } - image.Dispose(); + image.Dispose(); - image = new WuQuantizer().QuantizeImage(clone); + image = new WuQuantizer().QuantizeImage(clone); - // image = new OctreeQuantizer(255, 8).Quantize(image); - //} - //else - //{ - // image = new WuQuantizer().QuantizeImage((Bitmap)image); - //} + // image = new OctreeQuantizer(255, 8).Quantize(image); + } + else + { + image = new WuQuantizer().QuantizeImage((Bitmap)image); + } } return base.Save(path, image); diff --git a/src/ImageProcessor/Imaging/Helpers/Adjustments.cs b/src/ImageProcessor/Imaging/Helpers/Adjustments.cs index 672cb37f7..31634e7b5 100644 --- a/src/ImageProcessor/Imaging/Helpers/Adjustments.cs +++ b/src/ImageProcessor/Imaging/Helpers/Adjustments.cs @@ -13,6 +13,9 @@ namespace ImageProcessor.Imaging.Helpers using System; using System.Drawing; using System.Drawing.Imaging; + using System.Threading.Tasks; + + using ImageProcessor.Imaging.Colors; /// /// Provides reusable adjustment methods to apply to images. @@ -43,27 +46,30 @@ namespace ImageProcessor.Imaging.Helpers throw new ArgumentOutOfRangeException("percentage", "Percentage should be between 0 and 100."); } - Rectangle bounds = rectangle.HasValue ? rectangle.Value : new Rectangle(0, 0, source.Width, source.Height); - - ColorMatrix colorMatrix = new ColorMatrix(); - colorMatrix.Matrix00 = colorMatrix.Matrix11 = colorMatrix.Matrix22 = colorMatrix.Matrix44 = 1; - colorMatrix.Matrix33 = (float)percentage / 100; - - Bitmap alpha = new Bitmap(source.Width, source.Height); - alpha.SetResolution(source.HorizontalResolution, source.VerticalResolution); + float factor = (float)percentage / 100; + int width = source.Width; + int height = source.Height; - using (Graphics graphics = Graphics.FromImage(alpha)) + // Traditional examples using a color matrix alter the rgb values also. + using (FastBitmap bitmap = new FastBitmap(source)) { - graphics.Clear(Color.Transparent); - using (ImageAttributes imageAttributes = new ImageAttributes()) - { - imageAttributes.SetColorMatrix(colorMatrix); - graphics.DrawImage(source, bounds, 0, 0, source.Width, source.Height, GraphicsUnit.Pixel, imageAttributes); - } + // Loop through the pixels. + Parallel.For( + 0, + height, + y => + { + for (int x = 0; x < width; x++) + { + // ReSharper disable AccessToDisposedClosure + Color color = bitmap.GetPixel(x, y); + bitmap.SetPixel(x, y, Color.FromArgb(Convert.ToInt32(color.A * factor), color.R, color.G, color.B)); + // ReSharper restore AccessToDisposedClosure + } + }); } - - source.Dispose(); - return alpha; + + return (Bitmap)source; } /// diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs index 70573e988..343422ec5 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ColorMoment.cs @@ -1,8 +1,7 @@ -//using System.Runtime.CompilerServices; - + namespace ImageProcessor.Imaging.Quantizers.WuQuantizer { - struct ColorMoment + internal struct ColorMoment { public long Alpha; public long Red; @@ -44,7 +43,6 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer return c1; } - //[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(Pixel p) { byte pAlpha = p.Alpha; @@ -59,7 +57,6 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer Moment += pAlpha * pAlpha + pRed * pRed + pGreen * pGreen + pBlue * pBlue; } - //[MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddFast(ref ColorMoment c2) { Alpha += c2.Alpha; @@ -77,12 +74,12 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer public long WeightedDistance() { - return Amplitude() / Weight; + return this.Amplitude() / this.Weight; } public float Variance() { - var result = Moment - (float)Amplitude() / Weight; + float result = this.Moment - ((float)this.Amplitude() / this.Weight); return float.IsNaN(result) ? 0.0f : result; } } diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs index 9046c9082..39fc8c65c 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs @@ -8,8 +8,8 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer { private static IEnumerable IndexedPixels(ImageBuffer image, Pixel[] lookups, int alphaThreshold, PaletteColorHistory[] paletteHistogram) { - var lineIndexes = new byte[image.Image.Width]; - var lookup = new PaletteLookup(lookups); + byte[] lineIndexes = new byte[image.Image.Width]; + PaletteLookup lookup = new PaletteLookup(lookups); foreach (var pixelLine in image.PixelLines) { for (int pixelIndex = 0; pixelIndex < pixelLine.Length; pixelIndex++) @@ -21,8 +21,10 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer bestMatch = lookup.GetPaletteIndex(pixel); paletteHistogram[bestMatch].AddPixel(pixel); } + lineIndexes[pixelIndex] = bestMatch; } + yield return lineIndexes; } } @@ -43,6 +45,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer { palette.Entries[paletteColorIndex] = paletteHistogram[paletteColorIndex].ToNormalizedColor(); } + return palette; } } diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs index 1bdb2ea13..0f8ac9a21 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizerBase.cs @@ -17,7 +17,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer internal void Clear() { - Array.Clear(Moments, 0, SideSize*SideSize*SideSize*SideSize); + Array.Clear(Moments, 0, SideSize * SideSize * SideSize * SideSize); } } @@ -46,32 +46,35 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer var buffer = new ImageBuffer(image); if (histogram == null) + { histogram = new Histogram(); + } else + { histogram.Clear(); + } BuildHistogram(histogram, buffer, alphaThreshold, alphaFader); CalculateMoments(histogram.Moments); var cubes = SplitData(ref maxColors, histogram.Moments); var lookups = BuildLookups(cubes, histogram.Moments); - return GetQuantizedImage(buffer, maxColors, lookups, alphaThreshold); + return this.GetQuantizedImage(buffer, maxColors, lookups, alphaThreshold); } private static void BuildHistogram(Histogram histogram, ImageBuffer sourceImage, int alphaThreshold, int alphaFader) { - var moments = histogram.Moments; + ColorMoment[,,,] moments = histogram.Moments; - foreach(var pixelLine in sourceImage.PixelLines) + foreach (Pixel[] pixelLine in sourceImage.PixelLines) { - for (int pixelIndex = 0; pixelIndex < pixelLine.Length; pixelIndex++) + foreach (Pixel pixel in pixelLine) { - Pixel pixel = pixelLine[pixelIndex]; byte pixelAlpha = pixel.Alpha; if (pixelAlpha >= alphaThreshold) { if (pixelAlpha < 255) { - var alpha = pixel.Alpha + (pixel.Alpha % alphaFader); + int alpha = pixel.Alpha + (pixel.Alpha % alphaFader); pixelAlpha = (byte)(alpha > 255 ? 255 : alpha); } @@ -91,17 +94,17 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer private static void CalculateMoments(ColorMoment[, , ,] moments) { - var xarea = new ColorMoment[SideSize, SideSize]; - var area = new ColorMoment[SideSize]; - for (var alphaIndex = 1; alphaIndex < SideSize; alphaIndex++) + ColorMoment[,] xarea = new ColorMoment[SideSize, SideSize]; + ColorMoment[] area = new ColorMoment[SideSize]; + for (int alphaIndex = 1; alphaIndex < SideSize; alphaIndex++) { - for (var redIndex = 1; redIndex < SideSize; redIndex++) + for (int redIndex = 1; redIndex < SideSize; redIndex++) { Array.Clear(area, 0, area.Length); - for (var greenIndex = 1; greenIndex < SideSize; greenIndex++) + for (int greenIndex = 1; greenIndex < SideSize; greenIndex++) { - var line = new ColorMoment(); - for (var blueIndex = 1; blueIndex < SideSize; blueIndex++) + ColorMoment line = new ColorMoment(); + for (int blueIndex = 1; blueIndex < SideSize; blueIndex++) { line.AddFast(ref moments[alphaIndex, redIndex, greenIndex, blueIndex]); area[blueIndex].AddFast(ref line); @@ -220,10 +223,13 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer var result = 0.0f; byte? cutPoint = null; - for (var position = first; position < last; ++position) + for (byte position = first; position < last; ++position) { - var half = bottom + Top(cube, direction, position, moments); - if (half.Weight == 0) continue; + ColorMoment half = bottom + Top(cube, direction, position, moments); + if (half.Weight == 0) + { + continue; + } var temp = half.WeightedDistance(); @@ -275,28 +281,28 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer switch (direction) { case Alpha: - second.AlphaMinimum = first.AlphaMaximum = (byte) maxAlpha.Position; + second.AlphaMinimum = first.AlphaMaximum = (byte)maxAlpha.Position; second.RedMinimum = first.RedMinimum; second.GreenMinimum = first.GreenMinimum; second.BlueMinimum = first.BlueMinimum; break; case Red: - second.RedMinimum = first.RedMaximum = (byte) maxRed.Position; + second.RedMinimum = first.RedMaximum = (byte)maxRed.Position; second.AlphaMinimum = first.AlphaMinimum; second.GreenMinimum = first.GreenMinimum; second.BlueMinimum = first.BlueMinimum; break; case Green: - second.GreenMinimum = first.GreenMaximum = (byte) maxGreen.Position; + second.GreenMinimum = first.GreenMaximum = (byte)maxGreen.Position; second.AlphaMinimum = first.AlphaMinimum; second.RedMinimum = first.RedMinimum; second.BlueMinimum = first.BlueMinimum; break; case Blue: - second.BlueMinimum = first.BlueMaximum = (byte) maxBlue.Position; + second.BlueMinimum = first.BlueMaximum = (byte)maxBlue.Position; second.AlphaMinimum = first.AlphaMinimum; second.RedMinimum = first.RedMinimum; second.GreenMinimum = first.GreenMinimum;