diff --git a/Greenshot/Forms/QualityDialog.Designer.cs b/Greenshot/Forms/QualityDialog.Designer.cs index 0053918df..3455c102d 100644 --- a/Greenshot/Forms/QualityDialog.Designer.cs +++ b/Greenshot/Forms/QualityDialog.Designer.cs @@ -46,7 +46,6 @@ namespace Greenshot { /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(QualityDialog)); this.label_choosejpegquality = new System.Windows.Forms.Label(); this.textBoxJpegQuality = new System.Windows.Forms.TextBox(); this.trackBarJpegQuality = new System.Windows.Forms.TrackBar(); @@ -128,7 +127,7 @@ namespace Greenshot { this.Controls.Add(this.label_choosejpegquality); this.Controls.Add(this.textBoxJpegQuality); this.Controls.Add(this.trackBarJpegQuality); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Icon = GreenshotPlugin.Core.GreenshotResources.getGreenshotIcon(); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "JpegQualityDialog"; diff --git a/Greenshot/Greenshot.csproj b/Greenshot/Greenshot.csproj index 6b5cfe9d9..1e2774123 100644 --- a/Greenshot/Greenshot.csproj +++ b/Greenshot/Greenshot.csproj @@ -180,7 +180,6 @@ - diff --git a/Greenshot/Helpers/ImageOutput.cs b/Greenshot/Helpers/ImageOutput.cs index 8843de906..09405265d 100644 --- a/Greenshot/Helpers/ImageOutput.cs +++ b/Greenshot/Helpers/ImageOutput.cs @@ -101,11 +101,13 @@ namespace Greenshot.Helpers { } // If Quantizing is enable, overwrite the image to save with a 256 - color version - if (reduceColors) { + IColorQuantizer quantizer = ImageHelper.PrepareQuantize((Bitmap)imageToSave); + int colorCount = quantizer.GetColorCount(); + LOG.InfoFormat("Image with format {0} has {1} colors", imageToSave.PixelFormat, colorCount); + if (reduceColors || colorCount < 256) { try { LOG.Debug("Reducing colors on bitmap."); - Quantizer quantizer = new OctreeQuantizer(255,8); - imageToSave = quantizer.Quantize(imageToSave); + imageToSave = ImageHelper.Quantize((Bitmap)imageToSave, quantizer); // Make sure the "new" image is disposed disposeImage = true; } catch(Exception e) { @@ -202,7 +204,7 @@ namespace Greenshot.Helpers { isJPG = "JPG".Equals(extension.ToUpper()) || "JPEG".Equals(extension.ToUpper()); } - if(isJPG && conf.OutputFilePromptQuality) { + if(conf.OutputFilePromptQuality) { QualityDialog qualityDialog = new QualityDialog(isJPG); qualityDialog.ShowDialog(); quality = qualityDialog.Quality; diff --git a/Greenshot/Helpers/QuantizerHelper.cs b/Greenshot/Helpers/QuantizerHelper.cs deleted file mode 100644 index 690c29969..000000000 --- a/Greenshot/Helpers/QuantizerHelper.cs +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2012 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: http://getgreenshot.org/ - * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -using System; -using System.Collections; -using System.Drawing; -using System.Drawing.Imaging; -using System.Runtime.InteropServices; - -namespace Greenshot.Helpers { - /// - /// Quantizer is a method to reduce the colors in a bitmap. - /// Currently there is only 1 implementation: OctreeQuantizer - /// - public unsafe abstract class Quantizer { - /// - /// Construct the quantizer - /// - /// 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'. - /// - public Quantizer (bool singlePass) { - _singlePass = singlePass ; - } - - /// - /// Quantize an image and return the resulting output bitmap - /// - /// The image to quantize - /// A quantized version of the image - public Bitmap Quantize(Image source) { - // Get the size of the source image - int height = source.Height ; - int width = source.Width ; - - // And construct a rectangle from these dimensions - Rectangle bounds = new Rectangle(0, 0, width, height) ; - - // First off take a 32bpp copy of the image - Bitmap copy = new Bitmap ( width , height , PixelFormat.Format32bppArgb ) ; - - // And construct an 8bpp version - Bitmap output = new Bitmap ( width , height , PixelFormat.Format8bppIndexed ) ; - - // Now lock the bitmap into memory - using (Graphics g = Graphics.FromImage(copy)) { - g.PageUnit = GraphicsUnit.Pixel ; - - // Draw the source image onto the copy bitmap, - // which will effect a widening as appropriate. - g.DrawImage(source, bounds ) ; - } - - // Define a pointer to the bitmap data - BitmapData sourceData = null ; - - try { - // Get the source image bits and lock into memory - 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 - // all image pixels, build a data structure, and create a palette. - if ( !_singlePass ) - FirstPass ( sourceData , width , height ) ; - - // Then set the color palette on the output bitmap. I'm passing in the current palette - // as there's no way to construct a new, empty palette. - output.Palette = this.GetPalette ( output.Palette ) ; - - // Then call the second pass which actually does the conversion - SecondPass ( sourceData , output , width , height , bounds ) ; - } finally { - // Ensure that the bits are unlocked - copy.UnlockBits ( sourceData ); - copy.Dispose(); - } - - // Last but not least, return the output bitmap - return output; - } - - /// - /// Execute the first pass through the pixels in the image - /// - /// The source data - /// The width in pixels of the image - /// The height in pixels of the image - 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) - byte* pSourceRow = (byte*)sourceData.Scan0.ToPointer ( ) ; - Int32* pSourcePixel ; - - // Loop through each row - for ( int row = 0 ; row < height ; row++ ) { - // Set the source pixel to the first pixel in this row - pSourcePixel = (Int32*) pSourceRow ; - - // And loop through each column - for ( int col = 0 ; col < width ; col++ , pSourcePixel++ ) { - // Now I have the pixel, call the FirstPassQuantize function... - InitialQuantizePixel ( (Color32*)pSourcePixel ) ; - } - - // Add the stride to the source row - pSourceRow += sourceData.Stride ; - } - } - - /// - /// Execute a second pass through the bitmap - /// - /// The source bitmap, locked into memory - /// The output bitmap - /// The width in pixels of the image - /// The height in pixels of the image - /// The bounding rectangle - protected virtual void SecondPass ( BitmapData sourceData , Bitmap output , int width , int height , Rectangle bounds ) { - BitmapData outputData = null ; - - try { - // Lock the output bitmap into memory - outputData = output.LockBits ( bounds , ImageLockMode.WriteOnly , PixelFormat.Format8bppIndexed ) ; - - // Define the source data pointers. The source row is a byte to - // keep addition of the stride value easier (as this is in bytes) - byte* pSourceRow = (byte*)sourceData.Scan0.ToPointer ( ) ; - Int32* pSourcePixel = (Int32*)pSourceRow ; - Int32* pPreviousPixel = pSourcePixel ; - - // Now define the destination data pointers - byte* pDestinationRow = (byte*) outputData.Scan0.ToPointer(); - byte* pDestinationPixel = pDestinationRow ; - - // And convert the first pixel, so that I have values going into the loop - byte pixelValue = QuantizePixel ( (Color32*)pSourcePixel ) ; - - // Assign the value of the first pixel - *pDestinationPixel = pixelValue ; - - // Loop through each row - for ( int row = 0 ; row < height ; row++ ) { - // Set the source pixel to the first pixel in this row - pSourcePixel = (Int32*) pSourceRow ; - - // And set the destination pixel pointer to the first pixel in the row - pDestinationPixel = pDestinationRow ; - - // Loop through each pixel on this scan line - for ( int col = 0 ; col < width ; col++ , pSourcePixel++ , pDestinationPixel++ ) { - // 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 ( *pPreviousPixel != *pSourcePixel ) { - // Quantize the pixel - pixelValue = QuantizePixel ( (Color32*)pSourcePixel ) ; - - // And setup the previous pointer - pPreviousPixel = pSourcePixel ; - } - - // And set the pixel in the output - *pDestinationPixel = pixelValue ; - } - - // Add the stride to the source row - pSourceRow += sourceData.Stride ; - - // And to the destination row - pDestinationRow += outputData.Stride ; - } - } finally { - // Ensure that I unlock the output bits - output.UnlockBits ( outputData ) ; - } - } - - /// - /// Override this to 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 virtual void InitialQuantizePixel ( Color32* pixel ) { - } - - /// - /// Override this to process the pixel in the second pass of the algorithm - /// - /// The pixel to quantize - /// The quantized value - protected abstract byte QuantizePixel ( Color32* pixel ) ; - - /// - /// Retrieve the palette for the quantized image - /// - /// Any old palette, this is overrwritten - /// The new color palette - protected abstract ColorPalette GetPalette ( ColorPalette original ) ; - - /// - /// Flag used to indicate whether a single pass or two passes are needed for quantization. - /// - private bool _singlePass ; - - /// - /// Struct that defines a 32 bpp colour - /// - /// - /// This struct 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 layed out in memory - /// - [StructLayout(LayoutKind.Explicit)] - public struct Color32 { - /// - /// Holds the blue component of the colour - /// - [FieldOffset(0)] - public byte Blue ; - /// - /// Holds the green component of the colour - /// - [FieldOffset(1)] - public byte Green ; - /// - /// Holds the red component of the colour - /// - [FieldOffset(2)] - public byte Red ; - /// - /// Holds the alpha component of the colour - /// - [FieldOffset(3)] - public byte Alpha ; - - /// - /// Permits the color32 to be treated as an int32 - /// - [FieldOffset(0)] - public int ARGB ; - - /// - /// Return the color for this Color32 object - /// - public Color Color { - get { return Color.FromArgb ( Alpha , Red , Green , Blue ) ; } - } - } - } - - /// - /// Quantize using an Octree - /// - public unsafe class OctreeQuantizer : Quantizer { - /// - /// Construct the octree quantizer - /// - /// - /// 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 - /// - /// The maximum number of colors to return - /// The number of significant bits - public OctreeQuantizer ( int maxColors , int maxColorBits ) : base ( false ) { - if ( maxColors > 255 ) { - throw new ArgumentOutOfRangeException ( "maxColors" , maxColors , "The number of colors should be less than 256" ) ; - } - - if ( ( maxColorBits < 1 ) | ( maxColorBits > 8 ) ) { - throw new ArgumentOutOfRangeException ( "maxColorBits" , maxColorBits , "This should be between 1 and 8" ) ; - } - - // Construct the octree - _octree = new Octree ( maxColorBits ) ; - - _maxColors = maxColors ; - } - - /// - /// 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 ) { - // Add the color to the octree - _octree.AddColor ( pixel ) ; - } - - /// - /// 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 ) { - byte paletteIndex = (byte)_maxColors ; // The color at [_maxColors] is set to transparent - - // Get the palette index if this non-transparent - if ( pixel->Alpha > 0 ) { - paletteIndex = (byte)_octree.GetPaletteIndex ( pixel ) ; - } - - return paletteIndex ; - } - - /// - /// Retrieve the palette for the quantized image - /// - /// Any old palette, this is overrwritten - /// The new color palette - protected override ColorPalette GetPalette ( ColorPalette original ) { - // First off convert the octree to _maxColors colors - ArrayList palette = _octree.Palletize ( _maxColors - 1 ) ; - - // Then convert the palette based on those colors - for ( int index = 0 ; index < palette.Count ; index++ ) - original.Entries[index] = (Color)palette[index] ; - - // Add the transparent color - original.Entries[_maxColors] = Color.FromArgb ( 0 , 0 , 0 , 0 ) ; - - return original ; - } - - /// - /// Stores the tree - /// - private Octree _octree ; - - /// - /// Maximum allowed color depth - /// - private int _maxColors ; - - /// - /// Class which does the actual quantization - /// - private class Octree { - /// - /// Construct the octree - /// - /// The maximum number of significant bits in the image - public Octree ( int maxColorBits ) { - _maxColorBits = maxColorBits ; - _leafCount = 0 ; - _reducibleNodes = new OctreeNode[9] ; - _root = new OctreeNode ( 0 , _maxColorBits , this ) ; - _previousColor = 0 ; - _previousNode = null ; - } - - /// - /// Add a given color value to the octree - /// - /// - public void AddColor ( Color32* pixel ) { - // Check if this request is for the same color as the last - if ( _previousColor == pixel->ARGB ) { - // If so, check if I have a previous node setup. This will only ocurr if the first color in the image - // happens to be black, with an alpha component of zero. - if ( null == _previousNode ) { - _previousColor = pixel->ARGB ; - _root.AddColor ( pixel , _maxColorBits , 0 , this ) ; - } else { - // Just update the previous node - _previousNode.Increment ( pixel ) ; - } - } else { - _previousColor = pixel->ARGB ; - _root.AddColor ( pixel , _maxColorBits , 0 , this ) ; - } - } - - /// - /// Reduce the depth of the tree - /// - public void Reduce ( ) { - int index ; - - // Find the deepest level containing at least one reducible node - for ( index = _maxColorBits - 1 ; ( index > 0 ) && ( null == _reducibleNodes[index] ) ; index-- ) ; - - // Reduce the node most recently added to the list at level 'index' - OctreeNode node = _reducibleNodes[index] ; - _reducibleNodes[index] = node.NextReducible ; - - // Decrement the leaf count after reducing the node - _leafCount -= node.Reduce ( ) ; - - // And just in case I've reduced the last color to be added, and the next color to - // be added is the same, invalidate the previousNode... - _previousNode = null ; - } - - /// - /// Get/Set the number of leaves in the tree - /// - public int Leaves { - get { return _leafCount ; } - set { _leafCount = value ; } - } - - /// - /// Return the array of reducible nodes - /// - protected OctreeNode[] ReducibleNodes { - get { return _reducibleNodes ; } - } - - /// - /// Keep track of the previous node that was quantized - /// - /// The node last quantized - protected void TrackPrevious ( OctreeNode node ) { - _previousNode = node ; - } - - /// - /// Convert the nodes in the octree to a palette with a maximum of colorCount colors - /// - /// The maximum number of colors - /// An arraylist with the palettized colors - public ArrayList Palletize ( int colorCount ) { - while ( Leaves > colorCount ) { - Reduce(); - } - - // Now palettize the nodes - ArrayList palette = new ArrayList ( Leaves ) ; - int paletteIndex = 0 ; - _root.ConstructPalette ( palette , ref paletteIndex ) ; - - // And return the palette - return palette ; - } - - /// - /// Get the palette index for the passed color - /// - /// - /// - public int GetPaletteIndex ( Color32* pixel ) { - return _root.GetPaletteIndex ( pixel , 0 ) ; - } - - /// - /// Mask used when getting the appropriate pixels for a given node - /// - private static int[] mask = new int[8] { 0x80 , 0x40 , 0x20 , 0x10 , 0x08 , 0x04 , 0x02 , 0x01 } ; - - /// - /// The root of the octree - /// - private OctreeNode _root ; - - /// - /// Number of leaves in the tree - /// - private int _leafCount ; - - /// - /// Array of reducible nodes - /// - private OctreeNode[] _reducibleNodes ; - - /// - /// Maximum number of significant bits in the image - /// - private int _maxColorBits ; - - /// - /// Store the last node quantized - /// - private OctreeNode _previousNode ; - - /// - /// Cache the previous color quantized - /// - private int _previousColor ; - - /// - /// Class which encapsulates each node in the tree - /// - protected class OctreeNode { - /// - /// Construct the node - /// - /// The level in the tree = 0 - 7 - /// The number of significant color bits in the image - /// The tree to which this node belongs - public OctreeNode ( int level , int colorBits , Octree octree ) { - // Construct the new node - _leaf = ( level == colorBits ) ; - - _red = _green = _blue = 0 ; - _pixelCount = 0 ; - - // If a leaf, increment the leaf count - if ( _leaf ) { - octree.Leaves++ ; - _nextReducible = null ; - _children = null ; - } else { - // Otherwise add this to the reducible nodes - _nextReducible = octree.ReducibleNodes[level] ; - octree.ReducibleNodes[level] = this ; - _children = new OctreeNode[8] ; - } - } - - /// - /// Add a color into the tree - /// - /// The color - /// 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 ) { - // Update the color information if this is a leaf - if ( _leaf ) { - Increment ( pixel ) ; - // Setup the previous node - octree.TrackPrevious ( this ) ; - } else { - // 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 ) ) ; - - OctreeNode child = _children[index] ; - - if ( null == child ) { - // Create a new child node & store in the array - child = new OctreeNode ( level + 1 , colorBits , octree ) ; - _children[index] = child ; - } - - // Add the color to the child node - child.AddColor ( pixel , colorBits , level + 1 , octree ) ; - } - - } - - /// - /// Get/Set the next reducible node - /// - public OctreeNode NextReducible { - get { return _nextReducible ; } - set { _nextReducible = value ; } - } - - /// - /// Return the child nodes - /// - public OctreeNode[] Children { - get { return _children ; } - } - - /// - /// Reduce this node by removing all of its children - /// - /// The number of leaves removed - public int Reduce ( ) { - _red = _green = _blue = 0 ; - int children = 0 ; - - // Loop through all children and add their information to this node - for ( int index = 0 ; index < 8 ; index++ ) { - if ( null != _children[index] ) { - _red += _children[index]._red ; - _green += _children[index]._green ; - _blue += _children[index]._blue ; - _pixelCount += _children[index]._pixelCount ; - ++children ; - _children[index] = null ; - } - } - - // Now change this to a leaf node - _leaf = true ; - - // Return the number of nodes to decrement the leaf count by - return ( children - 1 ) ; - } - - /// - /// Traverse the tree, building up the color palette - /// - /// The palette - /// The current palette index - public void ConstructPalette ( ArrayList palette , ref int paletteIndex ) { - if ( _leaf ) { - // Consume the next palette index - _paletteIndex = paletteIndex++ ; - - // And set the color of the palette entry - palette.Add ( Color.FromArgb ( _red / _pixelCount , _green / _pixelCount , _blue / _pixelCount ) ) ; - } else { - // Loop through children looking for leaves - for ( int index = 0 ; index < 8 ; index++ ) { - if ( null != _children[index] ) { - _children[index].ConstructPalette ( palette , ref paletteIndex ) ; - } - } - } - } - - /// - /// Return the palette index for the passed color - /// - public int GetPaletteIndex ( Color32* pixel , int level ) { - int paletteIndex = _paletteIndex ; - - if ( !_leaf ) { - int shift = 7 - level ; - int index = ( ( pixel->Red & mask[level] ) >> ( shift - 2 ) ) | - ( ( pixel->Green & mask[level] ) >> ( shift - 1 ) ) | - ( ( pixel->Blue & mask[level] ) >> ( shift ) ) ; - - if ( null != _children[index] ) { - paletteIndex = _children[index].GetPaletteIndex ( pixel , level + 1 ) ; - } else { - throw new Exception ( "Didn't expect this!" ) ; - } - } - - return paletteIndex ; - } - - /// - /// Increment the pixel count and add to the color information - /// - public void Increment ( Color32* pixel ) { - _pixelCount++ ; - _red += pixel->Red ; - _green += pixel->Green ; - _blue += pixel->Blue ; - } - - /// - /// Flag indicating that this is a leaf node - /// - private bool _leaf ; - - /// - /// Number of pixels in this node - /// - private int _pixelCount ; - - /// - /// Red component - /// - private int _red ; - - /// - /// Green Component - /// - private int _green ; - - /// - /// Blue component - /// - private int _blue ; - - /// - /// Pointers to any child nodes - /// - private OctreeNode[] _children ; - - /// - /// Pointer to next reducible node - /// - private OctreeNode _nextReducible ; - - /// - /// The index of this node in the palette - /// - private int _paletteIndex ; - } - } - } -} diff --git a/GreenshotPlugin/Core/BitmapBuffer.cs b/GreenshotPlugin/Core/BitmapBuffer.cs index f1943162a..e12600cd2 100644 --- a/GreenshotPlugin/Core/BitmapBuffer.cs +++ b/GreenshotPlugin/Core/BitmapBuffer.cs @@ -136,7 +136,7 @@ namespace GreenshotPlugin.Core { // Set "this" rect to location 0,0 // as the Cloned Bitmap is only the part we want to work with this.rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); - } else if (!ImageHelper.SupportsPixelFormat(sourceBmp)) { + } else if (!ImageHelper.SupportsPixelFormat(sourceBmp) && PixelFormat.Format8bppIndexed != sourceBmp.PixelFormat) { throw new ArgumentException("Unsupported pixel format: " + sourceBmp.PixelFormat + " set clone to true!"); } else { this.bitmap = sourceBmp; @@ -268,11 +268,36 @@ namespace GreenshotPlugin.Core { Lock(); } } - - /** - * Retrieve the color at location x,y - * Before the first time this is called the Lock() should be called once! - */ + + /// + /// Retrieve the color index, for 8BPP, at location x,y + /// + /// X coordinate + /// Y Coordinate + /// index + public byte GetColorIndexAt(int x, int y) { + int offset = x*bytesPerPixel+y*stride; + return pointer[offset]; + } + + /// + /// Set the color index, for 8BPP, at location x,y + /// + /// X coordinate + /// Y Coordinate + /// Color index to set + public void SetColorIndexAt(int x, int y, byte color) { + int offset = x * bytesPerPixel + y * stride; + pointer[offset] = color; + } + + /// + /// Retrieve the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// X coordinate + /// Y Coordinate + /// Color public Color GetColorAt(int x, int y) { if(x>=0 && y>=0 && x + /// Retrieve the color, without alpha, at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// X coordinate + /// Y Coordinate + /// Color public Color GetColorAtWithoutAlpha(int x, int y) { if(x>=0 && y>=0 && x + /// Rotate the bitmap + /// + /// + /// + /// public static Bitmap RotateFlip(Bitmap sourceBitmap, RotateFlipType rotateFlipType) { Bitmap returnBitmap = Clone(sourceBitmap); returnBitmap.RotateFlip(rotateFlipType); return returnBitmap; } + + /// + /// Get a quantizer, so we can check if it pays off to quantize + /// + /// + /// IColorQuantizer + public static IColorQuantizer PrepareQuantize(Bitmap sourceBitmap) { + IColorQuantizer quantizer = new WuColorQuantizer(); + quantizer.Prepare(sourceBitmap); + using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, false)) { + bbbSrc.Lock(); + for (int y = 0; y < bbbSrc.Height; y++) { + for (int x = 0; x < bbbSrc.Width; x++) { + quantizer.AddColor(bbbSrc.GetColorAt(x, y)); + } + } + } + return quantizer; + } + + public static Bitmap Quantize(Bitmap sourceBitmap, IColorQuantizer quantizer) { + Bitmap result = new Bitmap(sourceBitmap.Width, sourceBitmap.Height, PixelFormat.Format8bppIndexed); + List palette = quantizer.GetPalette(255); + ColorPalette imagePalette = result.Palette; + // copies all color entries + for (Int32 index = 0; index < palette.Count; index++) { + imagePalette.Entries[index] = palette[index]; + } + result.Palette = imagePalette; + + using (BitmapBuffer bbbDest = new BitmapBuffer(result, false)) { + bbbDest.Lock(); + using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, false)) { + bbbSrc.Lock(); + for (int y = 0; y < bbbSrc.Height; y++) { + for (int x = 0; x < bbbSrc.Width; x++) { + Color originalColor = bbbSrc.GetColorAt(x, y); + Int32 paletteIndex = quantizer.GetPaletteIndex(originalColor); + bbbDest.SetColorIndexAt(x,y, (byte)paletteIndex); + } + } + } + } + return result; + } } } diff --git a/GreenshotPlugin/GreenshotPlugin.csproj b/GreenshotPlugin/GreenshotPlugin.csproj index 3b64e31a1..69362033b 100644 --- a/GreenshotPlugin/GreenshotPlugin.csproj +++ b/GreenshotPlugin/GreenshotPlugin.csproj @@ -199,6 +199,7 @@ +