Modified the Quantizer to Xiaolin Wu's, updated the BitmapBuffer to support 8BPP but you still need to know what to call.... (needs some additional refactoring)

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@1712 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2012-03-20 15:53:50 +00:00
parent 3250eee533
commit b464480bf6
7 changed files with 101 additions and 726 deletions

View file

@ -46,7 +46,6 @@ namespace Greenshot {
/// </summary>
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";

View file

@ -180,7 +180,6 @@
<Compile Include="Memento\IMemento.cs" />
<Compile Include="Memento\DrawableContainerBoundsChangeMemento.cs" />
<Compile Include="Memento\SurfaceBackgroundChangeMemento.cs" />
<Compile Include="Helpers\QuantizerHelper.cs" />
<Compile Include="Helpers\ScreenCaptureHelper.cs" />
<Compile Include="Helpers\UpdateHelper.cs" />
<Compile Include="Helpers\EnvironmentInfo.cs" />

View file

@ -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;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace Greenshot.Helpers {
/// <summary>
/// Quantizer is a method to reduce the colors in a bitmap.
/// Currently there is only 1 implementation: OctreeQuantizer
/// </summary>
public unsafe abstract class Quantizer {
/// <summary>
/// Construct the quantizer
/// </summary>
/// <param name="singlePass">If true, the quantization only needs to loop through the source pixels once</param>
/// <remarks>
/// 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'.
/// </remarks>
public Quantizer (bool singlePass) {
_singlePass = singlePass ;
}
/// <summary>
/// Quantize an image and return the resulting output bitmap
/// </summary>
/// <param name="source">The image to quantize</param>
/// <returns>A quantized version of the image</returns>
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;
}
/// <summary>
/// Execute the first pass through the pixels in the image
/// </summary>
/// <param name="sourceData">The source data</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
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 ;
}
}
/// <summary>
/// Execute a second pass through the bitmap
/// </summary>
/// <param name="sourceData">The source bitmap, locked into memory</param>
/// <param name="output">The output bitmap</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="bounds">The bounding rectangle</param>
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 ) ;
}
}
/// <summary>
/// Override this to process the pixel in the first pass of the algorithm
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <remarks>
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
/// </remarks>
protected virtual void InitialQuantizePixel ( Color32* pixel ) {
}
/// <summary>
/// Override this to process the pixel in the second pass of the algorithm
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <returns>The quantized value</returns>
protected abstract byte QuantizePixel ( Color32* pixel ) ;
/// <summary>
/// Retrieve the palette for the quantized image
/// </summary>
/// <param name="original">Any old palette, this is overrwritten</param>
/// <returns>The new color palette</returns>
protected abstract ColorPalette GetPalette ( ColorPalette original ) ;
/// <summary>
/// Flag used to indicate whether a single pass or two passes are needed for quantization.
/// </summary>
private bool _singlePass ;
/// <summary>
/// Struct that defines a 32 bpp colour
/// </summary>
/// <remarks>
/// 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
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
public struct Color32 {
/// <summary>
/// Holds the blue component of the colour
/// </summary>
[FieldOffset(0)]
public byte Blue ;
/// <summary>
/// Holds the green component of the colour
/// </summary>
[FieldOffset(1)]
public byte Green ;
/// <summary>
/// Holds the red component of the colour
/// </summary>
[FieldOffset(2)]
public byte Red ;
/// <summary>
/// Holds the alpha component of the colour
/// </summary>
[FieldOffset(3)]
public byte Alpha ;
/// <summary>
/// Permits the color32 to be treated as an int32
/// </summary>
[FieldOffset(0)]
public int ARGB ;
/// <summary>
/// Return the color for this Color32 object
/// </summary>
public Color Color {
get { return Color.FromArgb ( Alpha , Red , Green , Blue ) ; }
}
}
}
/// <summary>
/// Quantize using an Octree
/// </summary>
public unsafe class OctreeQuantizer : Quantizer {
/// <summary>
/// Construct the octree quantizer
/// </summary>
/// <remarks>
/// 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
/// </remarks>
/// <param name="maxColors">The maximum number of colors to return</param>
/// <param name="maxColorBits">The number of significant bits</param>
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 ;
}
/// <summary>
/// Process the pixel in the first pass of the algorithm
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <remarks>
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
/// </remarks>
protected override void InitialQuantizePixel ( Color32* pixel ) {
// Add the color to the octree
_octree.AddColor ( pixel ) ;
}
/// <summary>
/// Override this to process the pixel in the second pass of the algorithm
/// </summary>
/// <param name="pixel">The pixel to quantize</param>
/// <returns>The quantized value</returns>
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 ;
}
/// <summary>
/// Retrieve the palette for the quantized image
/// </summary>
/// <param name="original">Any old palette, this is overrwritten</param>
/// <returns>The new color palette</returns>
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 ;
}
/// <summary>
/// Stores the tree
/// </summary>
private Octree _octree ;
/// <summary>
/// Maximum allowed color depth
/// </summary>
private int _maxColors ;
/// <summary>
/// Class which does the actual quantization
/// </summary>
private class Octree {
/// <summary>
/// Construct the octree
/// </summary>
/// <param name="maxColorBits">The maximum number of significant bits in the image</param>
public Octree ( int maxColorBits ) {
_maxColorBits = maxColorBits ;
_leafCount = 0 ;
_reducibleNodes = new OctreeNode[9] ;
_root = new OctreeNode ( 0 , _maxColorBits , this ) ;
_previousColor = 0 ;
_previousNode = null ;
}
/// <summary>
/// Add a given color value to the octree
/// </summary>
/// <param name="pixel"></param>
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 ) ;
}
}
/// <summary>
/// Reduce the depth of the tree
/// </summary>
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 ;
}
/// <summary>
/// Get/Set the number of leaves in the tree
/// </summary>
public int Leaves {
get { return _leafCount ; }
set { _leafCount = value ; }
}
/// <summary>
/// Return the array of reducible nodes
/// </summary>
protected OctreeNode[] ReducibleNodes {
get { return _reducibleNodes ; }
}
/// <summary>
/// Keep track of the previous node that was quantized
/// </summary>
/// <param name="node">The node last quantized</param>
protected void TrackPrevious ( OctreeNode node ) {
_previousNode = node ;
}
/// <summary>
/// Convert the nodes in the octree to a palette with a maximum of colorCount colors
/// </summary>
/// <param name="colorCount">The maximum number of colors</param>
/// <returns>An arraylist with the palettized colors</returns>
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 ;
}
/// <summary>
/// Get the palette index for the passed color
/// </summary>
/// <param name="pixel"></param>
/// <returns></returns>
public int GetPaletteIndex ( Color32* pixel ) {
return _root.GetPaletteIndex ( pixel , 0 ) ;
}
/// <summary>
/// Mask used when getting the appropriate pixels for a given node
/// </summary>
private static int[] mask = new int[8] { 0x80 , 0x40 , 0x20 , 0x10 , 0x08 , 0x04 , 0x02 , 0x01 } ;
/// <summary>
/// The root of the octree
/// </summary>
private OctreeNode _root ;
/// <summary>
/// Number of leaves in the tree
/// </summary>
private int _leafCount ;
/// <summary>
/// Array of reducible nodes
/// </summary>
private OctreeNode[] _reducibleNodes ;
/// <summary>
/// Maximum number of significant bits in the image
/// </summary>
private int _maxColorBits ;
/// <summary>
/// Store the last node quantized
/// </summary>
private OctreeNode _previousNode ;
/// <summary>
/// Cache the previous color quantized
/// </summary>
private int _previousColor ;
/// <summary>
/// Class which encapsulates each node in the tree
/// </summary>
protected class OctreeNode {
/// <summary>
/// Construct the node
/// </summary>
/// <param name="level">The level in the tree = 0 - 7</param>
/// <param name="colorBits">The number of significant color bits in the image</param>
/// <param name="octree">The tree to which this node belongs</param>
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] ;
}
}
/// <summary>
/// Add a color into the tree
/// </summary>
/// <param name="pixel">The color</param>
/// <param name="colorBits">The number of significant color bits</param>
/// <param name="level">The level in the tree</param>
/// <param name="octree">The tree to which this node belongs</param>
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 ) ;
}
}
/// <summary>
/// Get/Set the next reducible node
/// </summary>
public OctreeNode NextReducible {
get { return _nextReducible ; }
set { _nextReducible = value ; }
}
/// <summary>
/// Return the child nodes
/// </summary>
public OctreeNode[] Children {
get { return _children ; }
}
/// <summary>
/// Reduce this node by removing all of its children
/// </summary>
/// <returns>The number of leaves removed</returns>
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 ) ;
}
/// <summary>
/// Traverse the tree, building up the color palette
/// </summary>
/// <param name="palette">The palette</param>
/// <param name="paletteIndex">The current palette index</param>
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 ) ;
}
}
}
}
/// <summary>
/// Return the palette index for the passed color
/// </summary>
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 ;
}
/// <summary>
/// Increment the pixel count and add to the color information
/// </summary>
public void Increment ( Color32* pixel ) {
_pixelCount++ ;
_red += pixel->Red ;
_green += pixel->Green ;
_blue += pixel->Blue ;
}
/// <summary>
/// Flag indicating that this is a leaf node
/// </summary>
private bool _leaf ;
/// <summary>
/// Number of pixels in this node
/// </summary>
private int _pixelCount ;
/// <summary>
/// Red component
/// </summary>
private int _red ;
/// <summary>
/// Green Component
/// </summary>
private int _green ;
/// <summary>
/// Blue component
/// </summary>
private int _blue ;
/// <summary>
/// Pointers to any child nodes
/// </summary>
private OctreeNode[] _children ;
/// <summary>
/// Pointer to next reducible node
/// </summary>
private OctreeNode _nextReducible ;
/// <summary>
/// The index of this node in the palette
/// </summary>
private int _paletteIndex ;
}
}
}
}

View file

@ -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;
@ -269,10 +269,35 @@ namespace GreenshotPlugin.Core {
}
}
/**
* Retrieve the color at location x,y
* Before the first time this is called the Lock() should be called once!
*/
/// <summary>
/// Retrieve the color index, for 8BPP, at location x,y
/// </summary>
/// <param name="x">X coordinate</param>
/// <param name="y">Y Coordinate</param>
/// <returns>index</returns>
public byte GetColorIndexAt(int x, int y) {
int offset = x*bytesPerPixel+y*stride;
return pointer[offset];
}
/// <summary>
/// Set the color index, for 8BPP, at location x,y
/// </summary>
/// <param name="x">X coordinate</param>
/// <param name="y">Y Coordinate</param>
/// <param name="color">Color index to set</param>
public void SetColorIndexAt(int x, int y, byte color) {
int offset = x * bytesPerPixel + y * stride;
pointer[offset] = color;
}
/// <summary>
/// Retrieve the color at location x,y
/// Before the first time this is called the Lock() should be called once!
/// </summary>
/// <param name="x">X coordinate</param>
/// <param name="y">Y Coordinate</param>
/// <returns>Color</returns>
public Color GetColorAt(int x, int y) {
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
int offset = x*bytesPerPixel+y*stride;
@ -283,10 +308,13 @@ namespace GreenshotPlugin.Core {
}
}
/**
* Retrieve the color at location x,y
* Before the first time this is called the Lock() should be called once!
*/
/// <summary>
/// Retrieve the color, without alpha, at location x,y
/// Before the first time this is called the Lock() should be called once!
/// </summary>
/// <param name="x">X coordinate</param>
/// <param name="y">Y Coordinate</param>
/// <returns>Color</returns>
public Color GetColorAtWithoutAlpha(int x, int y) {
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
int offset = x*bytesPerPixel+y*stride;
@ -393,6 +421,9 @@ namespace GreenshotPlugin.Core {
rIndex = 2;
bytesPerPixel = 3;
break;
case PixelFormat.Format8bppIndexed:
bytesPerPixel = 1;
break;
default:
throw new FormatException("Bitmap.Pixelformat."+bitmap.PixelFormat+" is currently not supported. Supported: Format32bpp(A)Rgb, Format24bppRgb");
}

View file

@ -993,10 +993,61 @@ namespace GreenshotPlugin.Core {
return newImage;
}
/// <summary>
/// Rotate the bitmap
/// </summary>
/// <param name="sourceBitmap"></param>
/// <param name="rotateFlipType"></param>
/// <returns></returns>
public static Bitmap RotateFlip(Bitmap sourceBitmap, RotateFlipType rotateFlipType) {
Bitmap returnBitmap = Clone(sourceBitmap);
returnBitmap.RotateFlip(rotateFlipType);
return returnBitmap;
}
/// <summary>
/// Get a quantizer, so we can check if it pays off to quantize
/// </summary>
/// <param name="sourceBitmap"></param>
/// <returns>IColorQuantizer</returns>
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<Color> 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;
}
}
}

View file

@ -199,6 +199,7 @@
<Compile Include="Core\NetworkHelper.cs" />
<Compile Include="Core\Objects.cs" />
<Compile Include="Core\PluginUtils.cs" />
<Compile Include="Core\QuantizerHelper.cs" />
<Compile Include="Core\SourceForgeHelper.cs" />
<Compile Include="Core\WindowCapture.cs" />
<Compile Include="Core\WindowsHelper.cs" />