diff --git a/src/ImageProcessor.Playground/ImageProcessor.Playground.csproj b/src/ImageProcessor.Playground/ImageProcessor.Playground.csproj index 22044e950..44d73c00b 100644 --- a/src/ImageProcessor.Playground/ImageProcessor.Playground.csproj +++ b/src/ImageProcessor.Playground/ImageProcessor.Playground.csproj @@ -34,6 +34,9 @@ prompt 4 + + + diff --git a/src/ImageProcessor.Playground/Program.cs b/src/ImageProcessor.Playground/Program.cs index 2bfd70ff8..bb6344a36 100644 --- a/src/ImageProcessor.Playground/Program.cs +++ b/src/ImageProcessor.Playground/Program.cs @@ -50,12 +50,12 @@ namespace ImageProcessor.PlayGround } // Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask2.png")); - //FileInfo fileInfo = new FileInfo(Path.Combine(resolvedPath, "rgba.png")); - IEnumerable files = GetFilesByExtensions(di, ".png"); + FileInfo fileInfo = new FileInfo(Path.Combine(resolvedPath, "rgba.png")); + //IEnumerable files = GetFilesByExtensions(di, ".png"); //IEnumerable files = GetFilesByExtensions(di, ".gif", ".webp", ".bmp", ".jpg", ".png", ".tif"); - foreach (FileInfo fileInfo in files) - { + //foreach (FileInfo fileInfo in files) + //{ byte[] photoBytes = File.ReadAllBytes(fileInfo.FullName); Console.WriteLine("Processing: " + fileInfo.Name); @@ -109,7 +109,7 @@ namespace ImageProcessor.PlayGround Console.WriteLine(@"Completed {0} in {1:s\.fff} secs with peak memory usage of {2}.", fileInfo.Name, stopwatch.Elapsed, Process.GetCurrentProcess().PeakWorkingSet64.ToString("#,#")); //Console.WriteLine("Processed: " + fileInfo.Name + " in " + stopwatch.ElapsedMilliseconds + "ms"); - } + //} Console.ReadLine(); } diff --git a/src/ImageProcessor/Imaging/FastBitmap.cs b/src/ImageProcessor/Imaging/FastBitmap.cs index 120286709..5b6fcb818 100644 --- a/src/ImageProcessor/Imaging/FastBitmap.cs +++ b/src/ImageProcessor/Imaging/FastBitmap.cs @@ -52,9 +52,9 @@ namespace ImageProcessor.Imaging private BitmapData bitmapData; /// - /// The pixel buffer for holding pixel data. + /// The position of the first pixel in the bitmap. /// - private byte* pixelBuffer; + private byte* pixelBase; /// /// A value indicating whether this instance of the given entity has been disposed. @@ -117,7 +117,7 @@ namespace ImageProcessor.Imaging /// private Color32* this[int x, int y] { - get { return (Color32*)(this.pixelBuffer + (y * this.bytesInARow) + (x * this.color32Size)); } + get { return (Color32*)(this.pixelBase + (y * this.bytesInARow) + (x * this.color32Size)); } } /// @@ -210,7 +210,7 @@ namespace ImageProcessor.Imaging this.Dispose(true); // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SupressFinalize to + // Therefore, you should call GC.SuppressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. @@ -290,8 +290,8 @@ namespace ImageProcessor.Imaging // Lock the bitmap this.bitmapData = this.bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); - // Copy the bitmap data across to the array for manipulation. - this.pixelBuffer = (byte*)this.bitmapData.Scan0.ToPointer(); + // Set the value to the first scan line + this.pixelBase = (byte*)this.bitmapData.Scan0.ToPointer(); } /// @@ -302,7 +302,7 @@ namespace ImageProcessor.Imaging // Copy the RGB values back to the bitmap and unlock the bitmap. this.bitmap.UnlockBits(this.bitmapData); this.bitmapData = null; - this.pixelBuffer = null; + this.pixelBase = null; } } } diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs index 91e5c975b..a0ee180f4 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/ImageBuffer.cs @@ -14,7 +14,6 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer using System; using System.Collections.Generic; using System.Drawing; - using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Runtime.InteropServices; @@ -43,7 +42,7 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer public Bitmap Image { get; private set; } /// - /// Gets the pixel lines. + /// Gets the enumerable pixel array representing each row of pixels. /// /// /// Thrown if the given image is not a 32 bit per pixel image. @@ -64,39 +63,20 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer int width = this.Image.Width; int height = this.Image.Height; - int[] buffer = new int[width]; Pixel[] pixels = new Pixel[width]; - //using (FastBitmap bitmap = new FastBitmap(this.Image)) - //{ - // for (int y = 0; y < height; y++) - // { - // for (int x = 0; x < width; x++) - // { - // Color color = bitmap.GetPixel(x, y); - // pixels[x] = new Pixel(color.A, color.R, color.G, color.B); - // } - // yield return pixels; - // } - //} - - for (int rowIndex = 0; rowIndex < height; rowIndex++) + using (FastBitmap bitmap = new FastBitmap(this.Image)) { - BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, rowIndex, width, rowIndex + 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); - try + for (int y = 0; y < height; y++) { - Marshal.Copy(data.Scan0, buffer, 0, width); - for (int pixelIndex = 0; pixelIndex < buffer.Length; pixelIndex++) + for (int x = 0; x < width; x++) { - pixels[pixelIndex] = new Pixel(buffer[pixelIndex]); + Color color = bitmap.GetPixel(x, y); + pixels[x] = new Pixel(color.A, color.R, color.G, color.B); } - } - finally - { - this.Image.UnlockBits(data); - } - yield return pixels; + yield return pixels; + } } } } @@ -105,31 +85,34 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer /// Updates the pixel indexes. /// /// - /// The line indexes. + /// The enumerable byte array representing each row of pixels. /// public void UpdatePixelIndexes(IEnumerable lineIndexes) { int width = this.Image.Width; int height = this.Image.Height; + int rowIndex = 0; - IEnumerator indexesIterator = lineIndexes.GetEnumerator(); - - for (int rowIndex = 0; rowIndex < height; rowIndex++) + BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); + try { - indexesIterator.MoveNext(); - - - BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, rowIndex, width, rowIndex + 1), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); - - try + IntPtr pixelBase = data.Scan0; + int scanWidth = data.Stride; + foreach (byte[] scanLine in lineIndexes) { - Marshal.Copy(indexesIterator.Current, 0, data.Scan0, width); - } - finally - { - this.Image.UnlockBits(data); + // TODO: Use unsafe code + Marshal.Copy(scanLine, 0, IntPtr.Add(pixelBase, scanWidth * rowIndex), width); + + if (++rowIndex >= height) + { + break; + } } } + finally + { + this.Image.UnlockBits(data); + } } } } diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs index d22486a6d..31b747ed6 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/PaletteLookup.cs @@ -82,11 +82,11 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer int bestDistance = int.MaxValue; byte bestMatch = 0; - foreach (var lookup in bucket) + foreach (LookupNode lookup in bucket) { - var lookupPixel = lookup.Pixel; + Pixel lookupPixel = lookup.Pixel; - var deltaAlpha = pixel.Alpha - lookupPixel.Alpha; + int deltaAlpha = pixel.Alpha - lookupPixel.Alpha; int distance = deltaAlpha * deltaAlpha; var deltaRed = pixel.Red - lookupPixel.Red; diff --git a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs index 39fc8c65c..038e2de3f 100644 --- a/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs +++ b/src/ImageProcessor/Imaging/Quantizers/WuQuantizer/WuQuantizer.cs @@ -1,16 +1,95 @@ -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Imaging.Quantizers.WuQuantizer { + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Imaging; + + /// + /// The wu quantizer. + /// public class WuQuantizer : WuQuantizerBase, IWuQuantizer { + /// + /// The get quantized image. + /// + /// + /// The image. + /// + /// + /// The color count. + /// + /// + /// The lookups. + /// + /// + /// The alpha threshold. + /// + /// + /// The . + /// + internal override Image GetQuantizedImage(ImageBuffer image, int colorCount, Pixel[] lookups, int alphaThreshold) + { + Bitmap result = new Bitmap(image.Image.Width, image.Image.Height, PixelFormat.Format8bppIndexed); + result.SetResolution(image.Image.HorizontalResolution, image.Image.VerticalResolution); + ImageBuffer resultBuffer = new ImageBuffer(result); + PaletteColorHistory[] paletteHistogram = new PaletteColorHistory[colorCount + 1]; + resultBuffer.UpdatePixelIndexes(IndexedPixels(image, lookups, alphaThreshold, paletteHistogram)); + result.Palette = BuildPalette(result.Palette, paletteHistogram); + return result; + } + + /// + /// The build palette. + /// + /// + /// The palette. + /// + /// + /// The palette histogram. + /// + /// + /// The . + /// + private static ColorPalette BuildPalette(ColorPalette palette, PaletteColorHistory[] paletteHistogram) + { + for (int paletteColorIndex = 0; paletteColorIndex < paletteHistogram.Length; paletteColorIndex++) + { + palette.Entries[paletteColorIndex] = paletteHistogram[paletteColorIndex].ToNormalizedColor(); + } + + return palette; + } + + /// + /// The indexed pixels. + /// + /// + /// The image. + /// + /// + /// The lookups. + /// + /// + /// The alpha threshold. + /// + /// + /// The palette histogram. + /// + /// + /// The . + /// private static IEnumerable IndexedPixels(ImageBuffer image, Pixel[] lookups, int alphaThreshold, PaletteColorHistory[] paletteHistogram) { byte[] lineIndexes = new byte[image.Image.Width]; PaletteLookup lookup = new PaletteLookup(lookups); - foreach (var pixelLine in image.PixelLines) + foreach (Pixel[] pixelLine in image.PixelLines) { for (int pixelIndex = 0; pixelIndex < pixelLine.Length; pixelIndex++) { @@ -28,25 +107,5 @@ namespace ImageProcessor.Imaging.Quantizers.WuQuantizer yield return lineIndexes; } } - - internal override Image GetQuantizedImage(ImageBuffer image, int colorCount, Pixel[] lookups, int alphaThreshold) - { - var result = new Bitmap(image.Image.Width, image.Image.Height, PixelFormat.Format8bppIndexed); - var resultBuffer = new ImageBuffer(result); - var paletteHistogram = new PaletteColorHistory[colorCount + 1]; - resultBuffer.UpdatePixelIndexes(IndexedPixels(image, lookups, alphaThreshold, paletteHistogram)); - result.Palette = BuildPalette(result.Palette, paletteHistogram); - return result; - } - - private static ColorPalette BuildPalette(ColorPalette palette, PaletteColorHistory[] paletteHistogram) - { - for (int paletteColorIndex = 0; paletteColorIndex < paletteHistogram.Length; paletteColorIndex++) - { - palette.Entries[paletteColorIndex] = paletteHistogram[paletteColorIndex].ToNormalizedColor(); - } - - return palette; - } } -} +} \ No newline at end of file