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:
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user