diff --git a/src/ImageProcessor.Playground/Program.cs b/src/ImageProcessor.Playground/Program.cs index 87a8cfbb5..6201c21c9 100644 --- a/src/ImageProcessor.Playground/Program.cs +++ b/src/ImageProcessor.Playground/Program.cs @@ -50,7 +50,7 @@ namespace ImageProcessor.PlayGround } Image mask = Image.FromFile(Path.Combine(resolvedPath, "mask2.png")); - IEnumerable files = GetFilesByExtensions(di, ".jpg"); + IEnumerable files = GetFilesByExtensions(di, ".gif"); //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(800, 0); + Size size = new Size(200, 0); ResizeLayer layer = new ResizeLayer(size, ResizeMode.Max, AnchorPosition.Center, false); //ContentAwareResizeLayer layer = new ContentAwareResizeLayer(size) @@ -79,7 +79,7 @@ namespace ImageProcessor.PlayGround //.Resize(new Size((int)(size.Width * 1.1), 0)) //.ContentAwareResize(layer) //.Constrain(size) - .Rotate(66) + //.Rotate(66) //.Mask(mask) //.Format(new PngFormat()) //.BackgroundColor(Color.Cyan) @@ -89,7 +89,7 @@ namespace ImageProcessor.PlayGround //.DetectEdges(new LaplacianOfGaussianEdgeFilter()) //.EntropyCrop() //.Filter(MatrixFilters.Invert) - //.Filter(MatrixFilters.Comic) + .Filter(MatrixFilters.Comic) //.Filter(MatrixFilters.HiSatch) //.Pixelate(8) //.GaussianSharpen(10) diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index dbbbffe88..d0f66a226 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -131,6 +131,9 @@ + + Code + @@ -177,8 +180,8 @@ - - + + @@ -196,7 +199,6 @@ - diff --git a/src/ImageProcessor/Imaging/PixelData.cs b/src/ImageProcessor/Imaging/Colors/Color32.cs similarity index 58% rename from src/ImageProcessor/Imaging/PixelData.cs rename to src/ImageProcessor/Imaging/Colors/Color32.cs index 513174c66..df62aef8f 100644 --- a/src/ImageProcessor/Imaging/PixelData.cs +++ b/src/ImageProcessor/Imaging/Colors/Color32.cs @@ -1,47 +1,67 @@ -// -------------------------------------------------------------------------------------------------------------------- -// +// -------------------------------------------------------------------------------------------------------------------- +// // Copyright (c) James South. // Licensed under the Apache License, Version 2.0. // // -// Contains the component parts that make up a single pixel. +// Structure that defines a 32 bit color // // -------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessor.Imaging +namespace ImageProcessor.Imaging.Colors { + using System.Drawing; using System.Runtime.InteropServices; /// - /// Contains the component parts that make up a single pixel. + /// Structure that defines a 32 bits per pixel color, used for pixel manipulation not for color conversion. /// + /// + /// This structure is used to read data from a 32 bits per pixel image + /// in memory, and is ordered in this manner as this is the way that + /// the data is laid out in memory + /// [StructLayout(LayoutKind.Explicit)] - public struct PixelData + public struct Color32 { /// - /// The blue component. + /// Holds the blue component of the colour /// [FieldOffset(0)] public byte B; /// - /// The green component. + /// Holds the green component of the colour /// [FieldOffset(1)] public byte G; /// - /// The red component. + /// Holds the red component of the colour /// [FieldOffset(2)] public byte R; /// - /// The alpha component. + /// Holds the alpha component of the colour /// [FieldOffset(3)] public byte A; + /// + /// Permits the color32 to be treated as a 32 bit integer. + /// + [FieldOffset(0)] + public int ARGB; + + /// + /// Gets the color for this Color32 object + /// + public Color Color + { + get { return Color.FromArgb(this.A, this.R, this.G, this.B); } + } + /// /// Indicates whether this instance and a specified object are equal. /// @@ -51,11 +71,11 @@ namespace ImageProcessor.Imaging /// Another object to compare to. public override bool Equals(object obj) { - if (obj is PixelData) + if (obj is Color32) { - PixelData pixelData = (PixelData)obj; + Color32 color32 = (Color32)obj; - return this.B == pixelData.B && this.G == pixelData.G & this.R == pixelData.R & this.A == pixelData.A; + return this.B == color32.B && this.G == color32.G & this.R == color32.R & this.A == color32.A; } return false; @@ -76,12 +96,12 @@ namespace ImageProcessor.Imaging /// Returns the hash code for the given instance. /// /// - /// The instance of to return the hash code for. + /// The instance of to return the hash code for. /// /// /// A 32-bit signed integer that is the hash code for this instance. /// - private int GetHashCode(PixelData obj) + private int GetHashCode(Color32 obj) { unchecked { @@ -93,4 +113,4 @@ namespace ImageProcessor.Imaging } } } -} \ No newline at end of file +} diff --git a/src/ImageProcessor/Imaging/FastBitmap.cs b/src/ImageProcessor/Imaging/FastBitmap.cs index e1bfda574..120286709 100644 --- a/src/ImageProcessor/Imaging/FastBitmap.cs +++ b/src/ImageProcessor/Imaging/FastBitmap.cs @@ -14,6 +14,8 @@ namespace ImageProcessor.Imaging using System.Drawing; using System.Drawing.Imaging; + using ImageProcessor.Imaging.Colors; + /// /// Allows fast access to 's pixel data. /// @@ -40,9 +42,9 @@ namespace ImageProcessor.Imaging private int bytesInARow; /// - /// The size of the pixel data. + /// The size of the color32 structure. /// - private int pixelDataSize; + private int color32Size; /// /// The bitmap data. @@ -111,11 +113,11 @@ namespace ImageProcessor.Imaging /// The y position of the pixel. /// /// - /// The . + /// The . /// - private PixelData* this[int x, int y] + private Color32* this[int x, int y] { - get { return (PixelData*)(this.pixelBuffer + (y * this.bytesInARow) + (x * this.pixelDataSize)); } + get { return (Color32*)(this.pixelBuffer + (y * this.bytesInARow) + (x * this.color32Size)); } } /// @@ -167,7 +169,7 @@ namespace ImageProcessor.Imaging throw new ArgumentOutOfRangeException("y", "Value cannot be less than zero or greater than the bitmap height."); } #endif - PixelData* data = this[x, y]; + Color32* data = this[x, y]; return Color.FromArgb(data->A, data->R, data->G, data->B); } @@ -193,7 +195,7 @@ namespace ImageProcessor.Imaging throw new ArgumentOutOfRangeException("y", "Value cannot be less than zero or greater than the bitmap height."); } #endif - PixelData* data = this[x, y]; + Color32* data = this[x, y]; data->R = color.R; data->G = color.G; data->B = color.B; @@ -278,8 +280,8 @@ namespace ImageProcessor.Imaging // Figure out the number of bytes in a row. This is rounded up to be a multiple // of 4 bytes, since a scan line in an image must always be a multiple of 4 bytes // in length. - this.pixelDataSize = sizeof(PixelData); - this.bytesInARow = bounds.Width * this.pixelDataSize; + this.color32Size = sizeof(Color32); + this.bytesInARow = bounds.Width * this.color32Size; if (this.bytesInARow % 4 != 0) { this.bytesInARow = 4 * ((this.bytesInARow / 4) + 1); diff --git a/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs b/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs index 49378385d..b21eb58bb 100644 --- a/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs +++ b/src/ImageProcessor/Imaging/Formats/FormatUtilities.cs @@ -135,7 +135,7 @@ namespace ImageProcessor.Imaging.Formats int frameCount = image.GetFrameCount(FrameDimension.Time); int last = frameCount - 1; int length = 0; - + List gifFrames = new List(); // Get the times stored in the gif. @@ -152,7 +152,7 @@ namespace ImageProcessor.Imaging.Formats image.SelectActiveFrame(FrameDimension.Time, i); // TODO: Get positions. - gifFrames.Add(new GifFrame { Delay = delay, Image = (Image)image.Clone() }); + gifFrames.Add(new GifFrame { Delay = delay, Image = new Bitmap(image) }); // Reset the position. if (i == last) diff --git a/src/ImageProcessor/Imaging/Formats/GifFormat.cs b/src/ImageProcessor/Imaging/Formats/GifFormat.cs index dffccb99b..8566e998b 100644 --- a/src/ImageProcessor/Imaging/Formats/GifFormat.cs +++ b/src/ImageProcessor/Imaging/Formats/GifFormat.cs @@ -16,6 +16,8 @@ namespace ImageProcessor.Imaging.Formats using System.IO; using System.Text; + using ImageProcessor.Imaging.Quantizers; + /// /// Provides the necessary information to support gif images. /// diff --git a/src/ImageProcessor/Imaging/Formats/PngFormat.cs b/src/ImageProcessor/Imaging/Formats/PngFormat.cs index cbfd938dd..23a3f300e 100644 --- a/src/ImageProcessor/Imaging/Formats/PngFormat.cs +++ b/src/ImageProcessor/Imaging/Formats/PngFormat.cs @@ -14,6 +14,8 @@ namespace ImageProcessor.Imaging.Formats using System.Drawing.Imaging; using System.IO; + using ImageProcessor.Imaging.Quantizers; + /// /// Provides the necessary information to support png images. /// diff --git a/src/ImageProcessor/Imaging/OctreeQuantizer.cs b/src/ImageProcessor/Imaging/Quantizers/OctreeQuantizer.cs similarity index 75% rename from src/ImageProcessor/Imaging/OctreeQuantizer.cs rename to src/ImageProcessor/Imaging/Quantizers/OctreeQuantizer.cs index e8ff086f0..34358a754 100644 --- a/src/ImageProcessor/Imaging/OctreeQuantizer.cs +++ b/src/ImageProcessor/Imaging/Quantizers/OctreeQuantizer.cs @@ -4,29 +4,33 @@ // Licensed under the Apache License, Version 2.0. // // -// Encapsulates methods to calculate the colour palette if an image using an octree pattern. +// Encapsulates methods to calculate the colour palette if an image using an Octree pattern. +// // // -------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessor.Imaging +namespace ImageProcessor.Imaging.Quantizers { using System; using System.Collections; using System.Drawing; using System.Drawing.Imaging; + using ImageProcessor.Imaging.Colors; + /// - /// Encapsulates methods to calculate the colour palette if an image using an octree pattern. + /// Encapsulates methods to calculate the colour palette if an image using an Octree pattern. + /// /// - public class OctreeQuantizer : Quantizer + public unsafe class OctreeQuantizer : Quantizer { /// - /// Stores the tree. + /// Stores the tree /// private readonly Octree octree; /// - /// The maximum allowed color depth. + /// Maximum allowed color depth /// private readonly int maxColors; @@ -34,7 +38,7 @@ namespace ImageProcessor.Imaging /// Initializes a new instance of the class. /// /// - /// The Octree quantizer is a two pass algorithm. The initial pass sets up the octree, + /// The Octree quantizer is a two pass algorithm. The initial pass sets up the Octree, /// the second pass quantizes a color based on the nodes in the tree /// /// @@ -56,22 +60,23 @@ namespace ImageProcessor.Imaging throw new ArgumentOutOfRangeException("maxColorBits", maxColorBits, "This should be between 1 and 8"); } - // Construct the octree + // Construct the Octree this.octree = new Octree(maxColorBits); + this.maxColors = maxColors; } /// - /// Override this to process the pixel in the first pass of the algorithm + /// Process the pixel in the first pass of the algorithm /// /// The pixel to quantize /// /// This function need only be overridden if your quantize algorithm needs two passes, /// such as an Octree quantizer. /// - protected override void InitialQuantizePixel(Color32 pixel) + protected override void InitialQuantizePixel(Color32* pixel) { - // Add the color to the octree + // Add the color to the Octree this.octree.AddColor(pixel); } @@ -79,16 +84,14 @@ namespace ImageProcessor.Imaging /// Override this to process the pixel in the second pass of the algorithm /// /// The pixel to quantize - /// - /// The quantized value - /// - protected override byte QuantizePixel(Color32 pixel) + /// The quantized value + protected override byte QuantizePixel(Color32* pixel) { // The color at [_maxColors] is set to transparent byte paletteIndex = (byte)this.maxColors; // Get the palette index if this non-transparent - if (pixel.Alpha > 0) + if (pixel->A > 0) { paletteIndex = (byte)this.octree.GetPaletteIndex(pixel); } @@ -100,41 +103,26 @@ namespace ImageProcessor.Imaging /// Retrieve the palette for the quantized image /// /// Any old palette, this is overwritten - /// - /// The new color palette - /// + /// The new color palette protected override ColorPalette GetPalette(ColorPalette original) { - // First off convert the octree to _maxColors colors + // First off convert the Octree to _maxColors colors ArrayList palette = this.octree.Palletize(this.maxColors - 1); // Then convert the palette based on those colors for (int index = 0; index < palette.Count; index++) { - Color testColor = (Color)palette[index]; - - // Test set transparent color when color transparency used - if (testColor.ToArgb() == Color.Transparent.ToArgb()) - { - testColor = Color.FromArgb(0, 0, 0, 0); - } - - original.Entries[index] = testColor; + original.Entries[index] = (Color)palette[index]; } - // Clear unused palette entries - for (int index = palette.Count; index < this.maxColors; index++) - { - original.Entries[index] = Color.FromArgb(255, 0, 0, 0); - } + // Add the transparent color + original.Entries[this.maxColors] = Color.FromArgb(0, 0, 0, 0); - // Add the transparent color when alpha transparency used - original.Entries[this.maxColors] = Color.FromArgb(0, Color.Transparent); return original; } /// - /// Describes a tree data structure in which each internal node has exactly eight children. + /// Class which does the actual quantization /// private class Octree { @@ -144,7 +132,7 @@ namespace ImageProcessor.Imaging private static readonly int[] Mask = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; /// - /// The root of the octree + /// The root of the Octree /// private readonly OctreeNode root; @@ -199,21 +187,29 @@ namespace ImageProcessor.Imaging } /// - /// Add a given colour value to the octree + /// Gets the array of reducible nodes + /// + private OctreeNode[] ReducibleNodes + { + get { return this.reducibleNodes; } + } + + /// + /// Add a given color value to the Octree /// /// - /// The color value to add. + /// The containing color information to add. /// - public void AddColor(Color32 pixel) + public void AddColor(Color32* pixel) { // Check if this request is for the same color as the last - if (this.previousColor == pixel.Argb) + if (this.previousColor == pixel->ARGB) { - // If so, check if I have a previous node setup. This will only ocurr if the first color in the image + // If so, check if I have a previous node setup. This will only occur if the first color in the image // happens to be black, with an alpha component of zero. if (null == this.previousNode) { - this.previousColor = pixel.Argb; + this.previousColor = pixel->ARGB; this.root.AddColor(pixel, this.maxColorBits, 0, this); } else @@ -224,16 +220,16 @@ namespace ImageProcessor.Imaging } else { - this.previousColor = pixel.Argb; + this.previousColor = pixel->ARGB; this.root.AddColor(pixel, this.maxColorBits, 0, this); } } /// - /// Convert the nodes in the octree to a palette with a maximum of colorCount colors + /// Convert the nodes in the Octree to a palette with a maximum of colorCount colors /// /// The maximum number of colors - /// An array-list with the palletized colors + /// An with the palletized colors public ArrayList Palletize(int colorCount) { while (this.Leaves > colorCount) @@ -241,7 +237,7 @@ namespace ImageProcessor.Imaging this.Reduce(); } - // Now palletized the nodes + // Now palletize the nodes ArrayList palette = new ArrayList(this.Leaves); int paletteIndex = 0; this.root.ConstructPalette(palette, ref paletteIndex); @@ -251,30 +247,19 @@ namespace ImageProcessor.Imaging } /// - /// Get the palette index for the passed colour. + /// Get the palette index for the passed color /// /// - /// The color to return the palette index for. + /// The containing the pixel data. /// /// - /// The palette index for the passed colour. + /// The index of the given structure. /// - public int GetPaletteIndex(Color32 pixel) + public int GetPaletteIndex(Color32* pixel) { return this.root.GetPaletteIndex(pixel, 0); } - /// - /// Return the array of reducible nodes - /// - /// - /// The array of . - /// - protected OctreeNode[] ReducibleNodes() - { - return this.reducibleNodes; - } - /// /// Keep track of the previous node that was quantized /// @@ -289,8 +274,6 @@ namespace ImageProcessor.Imaging /// private void Reduce() { - // Find the deepest level containing at least one reducible node - // for (index = _maxColorBits - 1; (index > 0) && (null == _reducibleNodes[index]); index--) ; // Find the deepest level containing at least one reducible node int index = this.maxColorBits - 1; while ((index > 0) && (null == this.reducibleNodes[index])) @@ -385,15 +368,14 @@ namespace ImageProcessor.Imaging else { // Otherwise add this to the reducible nodes - var repNodes = octree.ReducibleNodes(); - this.nextReducible = repNodes[level]; - repNodes[level] = this; + this.nextReducible = octree.ReducibleNodes[level]; + octree.ReducibleNodes[level] = this; this.children = new OctreeNode[8]; } } /// - /// Gets the next reducible node. + /// Gets the next reducible node /// public OctreeNode NextReducible { @@ -407,7 +389,7 @@ namespace ImageProcessor.Imaging /// The number of significant color bits /// The level in the tree /// The tree to which this node belongs - public void AddColor(Color32 pixel, int colorBits, int level, Octree octree) + public void AddColor(Color32* pixel, int colorBits, int level, Octree octree) { // Update the color information if this is a leaf if (this.leaf) @@ -419,26 +401,23 @@ namespace ImageProcessor.Imaging } else { - checked - { - // Go to the next level down in the tree - int shift = 7 - level; - int index = ((pixel.Red & Mask[level]) >> (shift - 2)) | - ((pixel.Green & Mask[level]) >> (shift - 1)) | - ((pixel.Blue & Mask[level]) >> shift); + // Go to the next level down in the tree + int shift = 7 - level; + int index = ((pixel->R & Mask[level]) >> (shift - 2)) | + ((pixel->G & Mask[level]) >> (shift - 1)) | + ((pixel->B & Mask[level]) >> shift); - OctreeNode child = this.children[index]; + OctreeNode child = this.children[index]; - if (null == child) - { - // Create a new child node and store in the array - child = new OctreeNode(level + 1, colorBits, octree); - this.children[index] = child; - } - - // Add the color to the child node - child.AddColor(pixel, colorBits, level + 1, octree); + if (null == child) + { + // Create a new child node & store in the array + child = new OctreeNode(level + 1, colorBits, octree); + this.children[index] = child; } + + // Add the color to the child node + child.AddColor(pixel, colorBits, level + 1, octree); } } @@ -501,38 +480,35 @@ namespace ImageProcessor.Imaging } /// - /// Return the palette index for the passed color. + /// Return the palette index for the passed color /// /// - /// The pixel. + /// The representing the pixel. /// /// /// The level. /// /// - /// The palette index for the passed color. + /// The representing the index of the pixel in the palette. /// - public int GetPaletteIndex(Color32 pixel, int level) + public int GetPaletteIndex(Color32* pixel, int level) { int index = this.paletteIndex; if (!this.leaf) { - checked - { - int shift = 7 - level; - int i = ((pixel.Red & Mask[level]) >> (shift - 2)) - | ((pixel.Green & Mask[level]) >> (shift - 1)) - | ((pixel.Blue & Mask[level]) >> shift); + int shift = 7 - level; + int pixelIndex = ((pixel->R & Mask[level]) >> (shift - 2)) | + ((pixel->G & Mask[level]) >> (shift - 1)) | + ((pixel->B & Mask[level]) >> shift); - if (null != this.children[i]) - { - index = this.children[i].GetPaletteIndex(pixel, level + 1); - } - else - { - throw new ArgumentException("Didn't expect this!"); - } + if (null != this.children[pixelIndex]) + { + index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1); + } + else + { + throw new Exception("Didn't expect this!"); } } @@ -543,16 +519,16 @@ namespace ImageProcessor.Imaging /// Increment the pixel count and add to the color information /// /// - /// The pixel. + /// The pixel to add. /// - public void Increment(Color32 pixel) + public void Increment(Color32* pixel) { this.pixelCount++; - this.red += pixel.Red; - this.green += pixel.Green; - this.blue += pixel.Blue; + this.red += pixel->R; + this.green += pixel->G; + this.blue += pixel->B; } } } } -} \ No newline at end of file +} diff --git a/src/ImageProcessor/Imaging/Quantizer.cs b/src/ImageProcessor/Imaging/Quantizers/Quantizer.cs similarity index 53% rename from src/ImageProcessor/Imaging/Quantizer.cs rename to src/ImageProcessor/Imaging/Quantizers/Quantizer.cs index e51ffce02..04252e1fb 100644 --- a/src/ImageProcessor/Imaging/Quantizer.cs +++ b/src/ImageProcessor/Imaging/Quantizers/Quantizer.cs @@ -4,53 +4,50 @@ // Licensed under the Apache License, Version 2.0. // // -// Defines the Quantizer type. +// Encapsulates methods to calculate the color palette of an image. +// // // -------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessor.Imaging +namespace ImageProcessor.Imaging.Quantizers { - using System; using System.Drawing; using System.Drawing.Imaging; - using System.Runtime.InteropServices; + + using ImageProcessor.Imaging.Colors; /// /// Encapsulates methods to calculate the color palette of an image. + /// /// - public abstract class Quantizer + public unsafe abstract class Quantizer { /// - /// The flag used to indicate whether a single pass or two passes are needed for quantization. + /// Flag used to indicate whether a single pass or two passes are needed for quantization. /// private readonly bool singlePass; /// - /// The size in bytes of the 32 bytes per pixel Color structure. - /// - private readonly int pixelSize; - - /// - /// Initializes a new instance of the Quantizer class. + /// Initializes a new instance of the class. /// /// - /// If set to , then the quantizer will loop through the source pixels once; - /// otherwise, . + /// If true, the quantization only needs to loop through the source pixels once /// + /// + /// If you construct this class with a true value for singlePass, then the code will, when quantizing your image, + /// only call the 'QuantizeImage' function. If two passes are required, the code will call 'InitialQuantizeImage' + /// and then 'QuantizeImage'. + /// protected Quantizer(bool singlePass) { this.singlePass = singlePass; - this.pixelSize = Marshal.SizeOf(typeof(Color32)); } /// - /// Quantizes the given Image and returns the resulting output - /// Bitmap. + /// Quantize an image and return the resulting output bitmap /// /// The image to quantize - /// - /// A quantized Bitmap version of the Image - /// + /// A quantized version of the image public Bitmap Quantize(Image source) { // Get the size of the source image @@ -62,11 +59,9 @@ namespace ImageProcessor.Imaging // First off take a 32bpp copy of the image Bitmap copy = new Bitmap(width, height, PixelFormat.Format32bppArgb); - copy.SetResolution(source.HorizontalResolution, source.VerticalResolution); // And construct an 8bpp version Bitmap output = new Bitmap(width, height, PixelFormat.Format8bppIndexed); - output.SetResolution(source.HorizontalResolution, source.VerticalResolution); // Now lock the bitmap into memory using (Graphics g = Graphics.FromImage(copy)) @@ -75,7 +70,7 @@ namespace ImageProcessor.Imaging // Draw the source image onto the copy bitmap, // which will effect a widening as appropriate. - g.DrawImage(source, bounds); + g.DrawImageUnscaled(source, bounds); } // Define a pointer to the bitmap data @@ -87,7 +82,7 @@ namespace ImageProcessor.Imaging sourceData = copy.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); // Call the FirstPass function if not a single pass algorithm. - // For something like an octree quantizer, this will run through + // For something like an Octree quantizer, this will run through // all image pixels, build a data structure, and create a palette. if (!this.singlePass) { @@ -120,25 +115,24 @@ namespace ImageProcessor.Imaging protected virtual void FirstPass(BitmapData sourceData, int width, int height) { // Define the source data pointers. The source row is a byte to - // keep addition of the stride value easier (as this is in bytes) - IntPtr sourceRow = sourceData.Scan0; + // keep addition of the stride value easier (as this is in bytes) + byte* sourceRow = (byte*)sourceData.Scan0.ToPointer(); // Loop through each row for (int row = 0; row < height; row++) { // Set the source pixel to the first pixel in this row - IntPtr sourcePixel = sourceRow; + int* sourcePixel = (int*)sourceRow; // And loop through each column - for (int col = 0; col < width; col++) + for (int col = 0; col < width; col++, sourcePixel++) { - this.InitialQuantizePixel(new Color32(sourcePixel)); - sourcePixel = (IntPtr)((long)sourcePixel + this.pixelSize); + // Now I have the pixel, call the FirstPassQuantize function... + this.InitialQuantizePixel((Color32*)sourcePixel); } - // Now I have the pixel, call the FirstPassQuantize function... // Add the stride to the source row - sourceRow = (IntPtr)((long)sourceRow + sourceData.Stride); + sourceRow += sourceData.Stride; } } @@ -161,55 +155,52 @@ namespace ImageProcessor.Imaging // Define the source data pointers. The source row is a byte to // keep addition of the stride value easier (as this is in bytes) - IntPtr sourceRow = sourceData.Scan0; - IntPtr sourcePixel = sourceRow; - IntPtr previousPixel = sourcePixel; + byte* sourceRow = (byte*)sourceData.Scan0.ToPointer(); + int* sourcePixel = (int*)sourceRow; + int* previousPixel = sourcePixel; // Now define the destination data pointers - IntPtr destinationRow = outputData.Scan0; - IntPtr destinationPixel = destinationRow; + byte* destinationRow = (byte*)outputData.Scan0.ToPointer(); + byte* destinationPixel = destinationRow; - // And convert the first pixel, so that I have values going into the loop. - byte pixelValue = this.QuantizePixel(new Color32(sourcePixel)); + // And convert the first pixel, so that I have values going into the loop + byte pixelValue = this.QuantizePixel((Color32*)sourcePixel); // Assign the value of the first pixel - Marshal.WriteByte(destinationPixel, pixelValue); + *destinationPixel = pixelValue; // Loop through each row for (int row = 0; row < height; row++) { // Set the source pixel to the first pixel in this row - sourcePixel = sourceRow; + sourcePixel = (int*)sourceRow; // And set the destination pixel pointer to the first pixel in the row destinationPixel = destinationRow; // Loop through each pixel on this scan line - for (int col = 0; col < width; col++) + for (int col = 0; col < width; col++, sourcePixel++, destinationPixel++) { // Check if this is the same as the last pixel. If so use that value // rather than calculating it again. This is an inexpensive optimisation. - if (Marshal.ReadInt32(previousPixel) != Marshal.ReadInt32(sourcePixel)) + if (*previousPixel != *sourcePixel) { // Quantize the pixel - pixelValue = this.QuantizePixel(new Color32(sourcePixel)); + pixelValue = this.QuantizePixel((Color32*)sourcePixel); // And setup the previous pointer previousPixel = sourcePixel; } // And set the pixel in the output - Marshal.WriteByte(destinationPixel, pixelValue); - - sourcePixel = (IntPtr)((long)sourcePixel + this.pixelSize); - destinationPixel = (IntPtr)((long)destinationPixel + 1); + *destinationPixel = pixelValue; } // Add the stride to the source row - sourceRow = (IntPtr)((long)sourceRow + sourceData.Stride); + sourceRow += sourceData.Stride; // And to the destination row - destinationRow = (IntPtr)((long)destinationRow + outputData.Stride); + destinationRow += outputData.Stride; } } finally @@ -227,7 +218,7 @@ namespace ImageProcessor.Imaging /// This function need only be overridden if your quantize algorithm needs two passes, /// such as an Octree quantizer. /// - protected virtual void InitialQuantizePixel(Color32 pixel) + protected virtual void InitialQuantizePixel(Color32* pixel) { } @@ -236,7 +227,7 @@ namespace ImageProcessor.Imaging /// /// The pixel to quantize /// The quantized value - protected abstract byte QuantizePixel(Color32 pixel); + protected abstract byte QuantizePixel(Color32* pixel); /// /// Retrieve the palette for the quantized image @@ -244,109 +235,5 @@ namespace ImageProcessor.Imaging /// Any old palette, this is overwritten /// The new color palette protected abstract ColorPalette GetPalette(ColorPalette original); - - /// - /// Structure that defines a 32 bit color - /// - /// - /// This structure is used to read data from a 32 bits per pixel image - /// in memory, and is ordered in this manner as this is the way that - /// the data is laid out in memory - /// - [StructLayout(LayoutKind.Explicit)] - public struct Color32 - { - /// - /// Holds the blue component of the colour - /// - [FieldOffset(0)] - private byte blue; - - /// - /// Holds the green component of the colour - /// - [FieldOffset(1)] - private byte green; - - /// - /// Holds the red component of the colour - /// - [FieldOffset(2)] - private byte red; - - /// - /// Holds the alpha component of the colour - /// - [FieldOffset(3)] - private byte alpha; - - /// - /// Permits the color32 to be treated as a 32 bit integer. - /// - [FieldOffset(0)] - private int argb; - - /// - /// Initializes a new instance of the Color32 structure. - /// - /// The pointer to the pixel. - public Color32(IntPtr sourcePixel) - { - this = (Color32)Marshal.PtrToStructure(sourcePixel, typeof(Color32)); - } - - /// - /// Gets or sets the blue component of the colour - /// - public byte Blue - { - get { return this.blue; } - set { this.blue = value; } - } - - /// - /// Gets or sets the green component of the colour - /// - public byte Green - { - get { return this.green; } - set { this.green = value; } - } - - /// - /// Gets or sets the red component of the colour - /// - public byte Red - { - get { return this.red; } - set { this.red = value; } - } - - /// - /// Gets or sets the alpha component of the colour - /// - public byte Alpha - { - get { return this.alpha; } - set { this.alpha = value; } - } - - /// - /// Gets or sets the ARGB component, permitting the color32 to be treated as a 32 bit integer. - /// - public int Argb - { - get { return this.argb; } - set { this.argb = value; } - } - - /// - /// Gets the color for this Color32 object - /// - public Color Color - { - get { return Color.FromArgb(this.Alpha, this.Red, this.Green, this.Blue); } - } - } } -} \ No newline at end of file +} diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs index b2e0bc80c..44fd69b2a 100644 --- a/src/ImageProcessor/Processors/Resize.cs +++ b/src/ImageProcessor/Processors/Resize.cs @@ -73,7 +73,7 @@ namespace ImageProcessor.Processors try { ResizeLayer resizeLayer = this.DynamicParameter; - + // Augment the layer with the extra information. resizeLayer.RestrictedSizes = this.RestrictedSizes; Size maxSize = new Size(); @@ -91,9 +91,13 @@ namespace ImageProcessor.Processors Resizer resizer = new Resizer(resizeLayer); newImage = (Bitmap)resizer.ResizeImage(image); - // Reassign the image. - image.Dispose(); - image = newImage; + // Check that the original image has not been returned. + if (newImage != image) + { + // Reassign the image. + image.Dispose(); + image = newImage; + } } catch (Exception ex) {