diff --git a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs b/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs index a28c3d32e3..d43edc3539 100644 --- a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs +++ b/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Quantizers { using System; using System.Collections.Generic; + using System.Numerics; /// /// Encapsulates methods to calculate the color palette if an image using an Octree pattern. @@ -18,6 +19,11 @@ namespace ImageSharp.Quantizers where TColor : struct, IPackedPixel where TPacked : struct { + /// + /// The pixel buffer, used to reduce allocations. + /// + private readonly byte[] pixelBuffer = new byte[4]; + /// /// Stores the tree /// @@ -43,7 +49,7 @@ namespace ImageSharp.Quantizers /// public override QuantizedImage Quantize(ImageBase image, int maxColors) { - this.colors = maxColors.Clamp(1, 256); + this.colors = maxColors.Clamp(1, 255); if (this.octree == null) { @@ -67,7 +73,7 @@ namespace ImageSharp.Quantizers protected override void InitialQuantizePixel(TColor pixel) { // Add the color to the Octree - this.octree.AddColor(pixel); + this.octree.AddColor(pixel, this.pixelBuffer); } /// @@ -79,7 +85,7 @@ namespace ImageSharp.Quantizers /// protected override byte QuantizePixel(TColor pixel) { - return (byte)this.octree.GetPaletteIndex(pixel); + return (byte)this.octree.GetPaletteIndex(pixel, this.pixelBuffer); } /// @@ -88,7 +94,7 @@ namespace ImageSharp.Quantizers /// /// The new color palette /// - protected override List GetPalette() + protected override TColor[] GetPalette() { return this.octree.Palletize(Math.Max(this.colors, 1)); } @@ -170,10 +176,9 @@ namespace ImageSharp.Quantizers /// /// Add a given color value to the Octree /// - /// - /// The containing color information to add. - /// - public void AddColor(TColor pixel) + /// The pixel data. + /// The buffer array. + public void AddColor(TColor pixel, byte[] buffer) { TPacked packed = pixel.PackedValue; @@ -185,18 +190,18 @@ namespace ImageSharp.Quantizers if (this.previousNode == null) { this.previousColor = packed; - this.root.AddColor(pixel, this.maxColorBits, 0, this); + this.root.AddColor(pixel, this.maxColorBits, 0, this, buffer); } else { // Just update the previous node - this.previousNode.Increment(pixel); + this.previousNode.Increment(pixel, buffer); } } else { this.previousColor = packed; - this.root.AddColor(pixel, this.maxColorBits, 0, this); + this.root.AddColor(pixel, this.maxColorBits, 0, this, buffer); } } @@ -207,7 +212,7 @@ namespace ImageSharp.Quantizers /// /// An with the palletized colors /// - public List Palletize(int colorCount) + public TColor[] Palletize(int colorCount) { while (this.Leaves > colorCount) { @@ -215,7 +220,8 @@ namespace ImageSharp.Quantizers } // Now palletize the nodes - List palette = new List(this.Leaves); + TColor[] palette = new TColor[colorCount + 1]; + int paletteIndex = 0; this.root.ConstructPalette(palette, ref paletteIndex); @@ -226,13 +232,14 @@ namespace ImageSharp.Quantizers /// /// Get the palette index for the passed color /// - /// The containing the pixel data. + /// The pixel data. + /// The buffer array. /// - /// The index of the given structure. + /// The . /// - public int GetPaletteIndex(TColor pixel) + public int GetPaletteIndex(TColor pixel, byte[] buffer) { - return this.root.GetPaletteIndex(pixel, 0); + return this.root.GetPaletteIndex(pixel, 0, buffer); } /// @@ -275,6 +282,11 @@ namespace ImageSharp.Quantizers /// protected class OctreeNode { + /// + /// Vector representing the maximum number of bytes + /// + private static readonly Vector4 MaxBytes = new Vector4(255); + /// /// Pointers to any child nodes /// @@ -363,12 +375,13 @@ namespace ImageSharp.Quantizers /// The number of significant color bits /// The level in the tree /// The tree to which this node belongs - public void AddColor(TColor pixel, int colorBits, int level, Octree octree) + /// The buffer array. + public void AddColor(TColor pixel, int colorBits, int level, Octree octree, byte[] buffer) { // Update the color information if this is a leaf if (this.leaf) { - this.Increment(pixel); + this.Increment(pixel, buffer); // Setup the previous node octree.TrackPrevious(this); @@ -377,11 +390,12 @@ namespace ImageSharp.Quantizers { // Go to the next level down in the tree int shift = 7 - level; - Color color = new Color(pixel.ToVector4()); - int index = ((color.A & Mask[0]) >> (shift - 3)) | - ((color.B & Mask[level + 1]) >> (shift - 2)) | - ((color.G & Mask[level + 1]) >> (shift - 1)) | - ((color.R & Mask[level + 1]) >> shift); + pixel.ToBytes(buffer, 0, ComponentOrder.XYZW); + + int index = ((buffer[3] & Mask[0]) >> (shift - 3)) | + ((buffer[2] & Mask[level + 1]) >> (shift - 2)) | + ((buffer[1] & Mask[level + 1]) >> (shift - 1)) | + ((buffer[0] & Mask[level + 1]) >> shift); OctreeNode child = this.children[index]; @@ -393,7 +407,7 @@ namespace ImageSharp.Quantizers } // Add the color to the child node - child.AddColor(pixel, colorBits, level + 1, octree); + child.AddColor(pixel, colorBits, level + 1, octree, buffer); } } @@ -433,13 +447,11 @@ namespace ImageSharp.Quantizers /// /// The palette /// The current palette index - public void ConstructPalette(List palette, ref int index) + public void ConstructPalette(TColor[] palette, ref int index) { if (this.leaf) { - // Consume the next palette index - this.paletteIndex = index++; - + // TODO: Test Vector4 here byte r = (this.red / this.pixelCount).ToByte(); byte g = (this.green / this.pixelCount).ToByte(); byte b = (this.blue / this.pixelCount).ToByte(); @@ -447,8 +459,11 @@ namespace ImageSharp.Quantizers // And set the color of the palette entry TColor pixel = default(TColor); - pixel.PackFromVector4(new Color(r, g, b, a).ToVector4()); - palette.Add(pixel); + pixel.PackFromBytes(r, g, b, a); + palette[index] = pixel; + + // Consume the next palette index + this.paletteIndex = index++; } else { @@ -468,25 +483,27 @@ namespace ImageSharp.Quantizers /// /// The representing the pixel. /// The level. + /// The buffer array. /// /// The representing the index of the pixel in the palette. /// - public int GetPaletteIndex(TColor pixel, int level) + public int GetPaletteIndex(TColor pixel, int level, byte[] buffer) { int index = this.paletteIndex; if (!this.leaf) { int shift = 7 - level; - Color color = new Color(pixel.ToVector4()); - int pixelIndex = ((color.A & Mask[0]) >> (shift - 3)) | - ((color.B & Mask[level + 1]) >> (shift - 2)) | - ((color.G & Mask[level + 1]) >> (shift - 1)) | - ((color.R & Mask[level + 1]) >> shift); + pixel.ToBytes(buffer, 0, ComponentOrder.XYZW); + + int pixelIndex = ((buffer[3] & Mask[0]) >> (shift - 3)) | + ((buffer[2] & Mask[level + 1]) >> (shift - 2)) | + ((buffer[1] & Mask[level + 1]) >> (shift - 1)) | + ((buffer[0] & Mask[level + 1]) >> shift); if (this.children[pixelIndex] != null) { - index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1); + index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1, buffer); } else { @@ -500,17 +517,16 @@ namespace ImageSharp.Quantizers /// /// Increment the pixel count and add to the color information /// - /// - /// The pixel to add. - /// - public void Increment(TColor pixel) + /// The pixel to add. + /// The buffer array. + public void Increment(TColor pixel, byte[] buffer) { + pixel.ToBytes(buffer, 0, ComponentOrder.XYZW); this.pixelCount++; - Color color = new Color(pixel.ToVector4()); - this.red += color.R; - this.green += color.G; - this.blue += color.B; - this.alpha += color.A; + this.red += buffer[0]; + this.green += buffer[1]; + this.blue += buffer[2]; + this.alpha += buffer[3]; } } } diff --git a/src/ImageSharp/Quantizers/Octree/Quantizer.cs b/src/ImageSharp/Quantizers/Octree/Quantizer.cs index d88832634c..36d68c2093 100644 --- a/src/ImageSharp/Quantizers/Octree/Quantizer.cs +++ b/src/ImageSharp/Quantizers/Octree/Quantizer.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Quantizers { - using System.Collections.Generic; using System.Threading.Tasks; /// @@ -47,7 +46,7 @@ namespace ImageSharp.Quantizers int height = image.Height; int width = image.Width; byte[] quantizedPixels = new byte[width * height]; - List palette; + TColor[] palette; using (PixelAccessor pixels = image.Lock()) { @@ -65,7 +64,7 @@ namespace ImageSharp.Quantizers this.SecondPass(pixels, quantizedPixels, width, height); } - return new QuantizedImage(width, height, palette.ToArray(), quantizedPixels); + return new QuantizedImage(width, height, palette, quantizedPixels); } /// @@ -97,18 +96,14 @@ namespace ImageSharp.Quantizers /// The height in pixels of the image protected virtual void SecondPass(PixelAccessor source, byte[] output, int width, int height) { - Parallel.For( - 0, - source.Height, - Bootstrapper.Instance.ParallelOptions, - y => - { - for (int x = 0; x < source.Width; x++) - { - TColor sourcePixel = source[x, y]; - output[(y * source.Width) + x] = this.QuantizePixel(sourcePixel); - } - }); + for (int y = 0; y < height; y++) + { + // And loop through each column + for (int x = 0; x < width; x++) + { + output[(y * source.Width) + x] = this.QuantizePixel(source[x, y]); + } + } } /// @@ -138,6 +133,6 @@ namespace ImageSharp.Quantizers /// /// The new color palette /// - protected abstract List GetPalette(); + protected abstract TColor[] GetPalette(); } } \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs index 73b7f1af29..d8d5d0549a 100644 --- a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs +++ b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs @@ -7,8 +7,6 @@ namespace ImageSharp.Quantizers { using System; using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; /// /// Encapsulates methods to create a quantized image based upon the given palette. @@ -20,6 +18,11 @@ namespace ImageSharp.Quantizers where TColor : struct, IPackedPixel where TPacked : struct { + /// + /// The pixel buffer, used to reduce allocations. + /// + private readonly byte[] pixelBuffer = new byte[4]; + /// /// A lookup table for colors /// @@ -43,15 +46,18 @@ namespace ImageSharp.Quantizers if (palette == null) { Color[] constants = ColorConstants.WebSafeColors; - List safe = new List { default(TColor) }; - foreach (Color c in constants) + TColor[] safe = new TColor[constants.Length + 1]; + + for (int i = 0; i < constants.Length; i++) { + Color c = constants[i]; + c.ToBytes(this.pixelBuffer, 0, ComponentOrder.XYZW); TColor packed = default(TColor); - packed.PackFromVector4(c.ToVector4()); - safe.Add(packed); + packed.PackFromBytes(this.pixelBuffer[0], this.pixelBuffer[0], this.pixelBuffer[0], this.pixelBuffer[0]); + safe[i] = packed; } - this.colors = safe.ToArray(); + this.colors = safe; } else { @@ -62,7 +68,7 @@ namespace ImageSharp.Quantizers /// public override QuantizedImage Quantize(ImageBase image, int maxColors) { - Array.Resize(ref this.colors, maxColors.Clamp(1, 256)); + Array.Resize(ref this.colors, maxColors.Clamp(1, 255)); return base.Quantize(image, maxColors); } @@ -80,21 +86,20 @@ namespace ImageSharp.Quantizers else { // Not found - loop through the palette and find the nearest match. - Color color = new Color(pixel.ToVector4()); - + pixel.ToBytes(this.pixelBuffer, 0, ComponentOrder.XYZW); int leastDistance = int.MaxValue; - int red = color.R; - int green = color.G; - int blue = color.B; - int alpha = color.A; + int red = this.pixelBuffer[0]; + int green = this.pixelBuffer[1]; + int blue = this.pixelBuffer[2]; + int alpha = this.pixelBuffer[3]; for (int index = 0; index < this.colors.Length; index++) { - Color paletteColor = new Color(this.colors[index].ToVector4()); - int redDistance = paletteColor.R - red; - int greenDistance = paletteColor.G - green; - int blueDistance = paletteColor.B - blue; - int alphaDistance = paletteColor.A - alpha; + this.colors[index].ToBytes(this.pixelBuffer, 0, ComponentOrder.XYZW); + int redDistance = this.pixelBuffer[0] - red; + int greenDistance = this.pixelBuffer[1] - green; + int blueDistance = this.pixelBuffer[2] - blue; + int alphaDistance = this.pixelBuffer[3] - alpha; int distance = (redDistance * redDistance) + (greenDistance * greenDistance) + @@ -122,9 +127,9 @@ namespace ImageSharp.Quantizers } /// - protected override List GetPalette() + protected override TColor[] GetPalette() { - return this.colors.ToList(); + return this.colors; } } } \ No newline at end of file