Added a BColorQuantizer class to the "shared kit" based on a patch by David

Powel. The functionality was written by Jeff Prosise and Davide Pizzolato and
has been cleaned up and adopted to the Haiku coding style by David Powell and
myself. The class can perform a color quantization for generating the optimal
color index palette (with given maximum size) from a given RGBA image.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27221 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2008-08-28 16:33:12 +00:00
parent 910c4a0106
commit 740fe892ac
2 changed files with 363 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
/* === C R E D I T S & D I S C L A I M E R S ==============
* Permission is given by the author to freely redistribute and include
* this code in any program as long as this credit is given where due.
*
* CQuantizer (c) 1996-1997 Jeff Prosise
*
* 31/08/2003 Davide Pizzolato - www.xdp.it
* - fixed minor bug in ProcessImage when bpp<=8
* - better color reduction to less than 16 colors
*
* COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT
* WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT
* LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE
* RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU.
* SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL
* DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
* SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN
* ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED
* HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
*
* Use at your own risk!
* ==========================================================
*
* Modified for use with Haiku by David Powell & Stephan Aßmus.
*/
#ifndef COLOR_QUANTIZER_H
#define COLOR_QUANTIZER_H
#include <SupportDefs.h>
namespace BPrivate {
typedef struct _RGBA {
uint8 b;
uint8 g;
uint8 r;
uint8 a;
} RGBA;
class BColorQuantizer {
public:
BColorQuantizer(uint32 maxColors,
uint32 bitsPerColor);
virtual ~BColorQuantizer();
bool ProcessImage(const uint8* const * rowPtrs, int width,
int height);
uint32 GetColorCount() const;
void GetColorTable(RGBA* table) const;
private:
struct Node;
private:
void _AddColor(Node** _node, uint8 r, uint8 g, uint8 b,
uint8 a, uint32 bitsPerColor, uint32 level,
uint32* _leafCount, Node** reducibleNodes);
Node* _CreateNode(uint32 level, uint32 bitsPerColor,
uint32* _leafCount, Node** reducibleNodes);
void _ReduceTree(uint32 bitsPerColor, uint32* _leafCount,
Node** reducibleNodes);
void _DeleteTree(Node** _node);
void _GetPaletteColors(Node* node, RGBA* table,
uint32* pIndex, uint32* pSum) const;
private:
Node* fTree;
uint32 fLeafCount;
Node* fReducibleNodes[9];
uint32 fMaxColors;
uint32 fOutputMaxColors;
uint32 fBitsPerColor;
};
} // namespace BPrivate
using BPrivate::BColorQuantizer;
using BPrivate::RGBA;
#endif // COLOR_QUANTIZER_H
+278
View File
@@ -0,0 +1,278 @@
/* === C R E D I T S & D I S C L A I M E R S ==============
* Permission is given by the author to freely redistribute and include
* this code in any program as long as this credit is given where due.
*
* CQuantizer (c) 1996-1997 Jeff Prosise
*
* 31/08/2003 Davide Pizzolato - www.xdp.it
* - fixed minor bug in ProcessImage when bpp<=8
* - better color reduction to less than 16 colors
*
* COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT
* WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT
* LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE
* RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU.
* SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL
* DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
* SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN
* ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED
* HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
*
* Use at your own risk!
* ==========================================================
*
* Modified for use with Haiku by David Powell & Stephan Aßmus.
*/
#include "ColorQuantizer.h"
#include <stddef.h>
#include <stdlib.h>
static inline uint8
clip(float component)
{
if (component > 255.0)
return 255;
return (uint8)(component + 0.5f);
}
struct BColorQuantizer::Node {
bool isLeaf; // TRUE if node has no children
uint32 pixelCount; // Number of pixels represented by this leaf
uint32 sumR; // Sum of red components
uint32 sumG; // Sum of green components
uint32 sumB; // Sum of blue components
uint32 sumA; // Sum of alpha components
Node* child[8]; // Pointers to child nodes
Node* next; // Pointer to next reducible node
};
BColorQuantizer::BColorQuantizer(uint32 maxColors, uint32 bitsPerColor)
: fTree(NULL),
fLeafCount(0),
fMaxColors(maxColors),
fOutputMaxColors(maxColors),
fBitsPerColor(bitsPerColor)
{
// override parameters if out of range
if (fBitsPerColor > 8)
fBitsPerColor = 8;
if (fMaxColors < 16)
fMaxColors = 16;
for (int i = 0; i <= (int)fBitsPerColor; i++)
fReducibleNodes[i] = NULL;
}
BColorQuantizer::~BColorQuantizer()
{
if (fTree != NULL)
_DeleteTree(&fTree);
}
bool
BColorQuantizer::ProcessImage(const uint8* const * rowPtrs, int width,
int height)
{
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x += 3) {
uint8 b = rowPtrs[y][x];
uint8 g = rowPtrs[y][x + 1];
uint8 r = rowPtrs[y][x + 2];
_AddColor(&fTree, r, g, b, 0, fBitsPerColor, 0, &fLeafCount,
fReducibleNodes);
while (fLeafCount > fMaxColors)
_ReduceTree(fBitsPerColor, &fLeafCount, fReducibleNodes);
}
}
return true;
}
uint32
BColorQuantizer::GetColorCount() const
{
return fLeafCount;
}
void
BColorQuantizer::GetColorTable(RGBA* table) const
{
uint32 index = 0;
if (fOutputMaxColors < 16) {
uint32 sums[16];
RGBA tmpPalette[16];
_GetPaletteColors(fTree, tmpPalette, &index, sums);
if (fLeafCount > fOutputMaxColors) {
for (uint32 j = 0; j < fOutputMaxColors; j++) {
uint32 a = (j * fLeafCount) / fOutputMaxColors;
uint32 b = ((j + 1) * fLeafCount) / fOutputMaxColors;
uint32 nr = 0;
uint32 ng = 0;
uint32 nb = 0;
uint32 na = 0;
uint32 ns = 0;
for (uint32 k = a; k < b; k++){
nr += tmpPalette[k].r * sums[k];
ng += tmpPalette[k].g * sums[k];
nb += tmpPalette[k].b * sums[k];
na += tmpPalette[k].a * sums[k];
ns += sums[k];
}
table[j].r = clip((float)nr / ns);
table[j].g = clip((float)ng / ns);
table[j].b = clip((float)nb / ns);
table[j].a = clip((float)na / ns);
}
} else {
memcpy(table, tmpPalette, fLeafCount * sizeof(RGBA));
}
} else {
_GetPaletteColors(fTree, table, &index, NULL);
}
}
// #pragma mark - private
void
BColorQuantizer::_AddColor(Node** _node, uint8 r, uint8 g, uint8 b, uint8 a,
uint32 bitsPerColor, uint32 level, uint32* _leafCount,
Node** reducibleNodes)
{
static const uint8 kMask[8]
= {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
// If the node doesn't exist, create it.
if (*_node == NULL)
*_node = _CreateNode(level, bitsPerColor, _leafCount, reducibleNodes);
// Update color information if it's a leaf node.
if ((*_node)->isLeaf) {
(*_node)->pixelCount++;
(*_node)->sumR += r;
(*_node)->sumG += g;
(*_node)->sumB += b;
(*_node)->sumA += a;
} else {
// Recurse a level deeper if the node is not a leaf.
int shift = 7 - level;
int index = (((r & kMask[level]) >> shift) << 2) |
(((g & kMask[level]) >> shift) << 1) |
(( b & kMask[level]) >> shift);
_AddColor(&((*_node)->child[index]), r, g, b, a, bitsPerColor,
level + 1, _leafCount, reducibleNodes);
}
}
BColorQuantizer::Node*
BColorQuantizer::_CreateNode(uint32 level, uint32 bitsPerColor,
uint32* _leafCount, Node** reducibleNodes)
{
Node* node = (Node*)calloc(1, sizeof(Node));
if (node == NULL)
return NULL;
node->isLeaf = (level == bitsPerColor) ? true : false;
if (node->isLeaf)
(*_leafCount)++;
else {
node->next = reducibleNodes[level];
reducibleNodes[level] = node;
}
return node;
}
void
BColorQuantizer::_ReduceTree(uint32 bitsPerColor, uint32* _leafCount,
Node** reducibleNodes)
{
int i = bitsPerColor - 1;
// Find the deepest level containing at least one reducible node.
for (; i > 0 && reducibleNodes[i] == NULL; i--)
;
// Reduce the node most recently added to the list at level i.
Node* node = reducibleNodes[i];
reducibleNodes[i] = node->next;
uint32 sumR = 0;
uint32 sumG = 0;
uint32 sumB = 0;
uint32 sumA = 0;
uint32 childCount = 0;
for (i = 0; i < 8; i++) {
if (node->child[i] != NULL) {
sumR += node->child[i]->sumR;
sumG += node->child[i]->sumG;
sumB += node->child[i]->sumB;
sumA += node->child[i]->sumA;
node->pixelCount += node->child[i]->pixelCount;
free(node->child[i]);
node->child[i] = NULL;
childCount++;
}
}
node->isLeaf = true;
node->sumR = sumR;
node->sumG = sumG;
node->sumB = sumB;
node->sumA = sumA;
*_leafCount -= (childCount - 1);
}
void
BColorQuantizer::_DeleteTree(Node** _node)
{
for (int i = 0; i < 8; i++) {
if ((*_node)->child[i] != NULL)
_DeleteTree(&((*_node)->child[i]));
}
free(*_node);
*_node = NULL;
}
void
BColorQuantizer::_GetPaletteColors(Node* node, RGBA* table, uint32* _index,
uint32* sums) const
{
if (node == NULL)
return;
if (node->isLeaf) {
table[*_index].r = clip((float)node->sumR / node->pixelCount);
table[*_index].g = clip((float)node->sumG / node->pixelCount);
table[*_index].b = clip((float)node->sumB / node->pixelCount);
table[*_index].a = clip((float)node->sumA / node->pixelCount);
if (sums)
sums[*_index] = node->pixelCount;
(*_index)++;
} else {
for (int i = 0; i < 8; i++) {
if (node->child[i] != NULL)
_GetPaletteColors(node->child[i], table, _index, sums);
}
}
}