This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common/imagelib/img_quant.old

363 lines
8.2 KiB
Plaintext

/*
img_quant.c - image quantizer. based on hl2 beta original code
Copyright (C) 2011 Uncle Mike
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 3 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.
*/
#include "imagelib.h"
#define MAX_PAL_SIZE 256
#define MAX_NUM_NODES ( MAX_PAL_SIZE * 8 + 1 )
typedef struct quantnode_s
{
qboolean isLeaf;
int timesUsed;
vec3_t cumulativeColor;
int paletteIndex;
struct quantnode_s *children[8];
struct quantnode_s *parent;
struct quantnode_s *next; // next in the priority list for this level of the tree.
// also next for the free list.
} quantnode_t;
static quantnode_t nodePool[MAX_NUM_NODES];
static int nextFreeID;
static quantnode_t *freeList;
static quantnode_t *root;
static int numColorsUsed;
static int desiredNumColors;
static byte insertColor[3];
static int currNumPalIndices;
static byte *pal;
static int debugNumNodes = 0;
static quantnode_t *priorityQueues[9]; // not prioritized for now.
static quantnode_t *AllocNode()
{
quantnode_t *node;
debugNumNodes++;
if( nextFreeID < MAX_NUM_NODES )
{
node = &nodePool[nextFreeID];
nextFreeID++;
}
else
{
node = freeList;
if( !node )
{
ASSERT( node );
return NULL;
}
freeList = freeList->next;
}
Q_memset( node, 0, sizeof( quantnode_t ));
return node;
}
static void FreeNode( quantnode_t *node )
{
debugNumNodes--;
node->next = freeList;
freeList = node;
}
static void InitNodeAllocation( void )
{
freeList = NULL;
nextFreeID = 0;
}
static void RemoveFromPriorityQueue( int level, quantnode_t *node )
{
quantnode_t *searchNode, *prev;
quantnode_t dummy;
dummy.next = priorityQueues[level];
prev = &dummy;
for( searchNode = dummy.next; searchNode; searchNode = searchNode->next )
{
if( searchNode == node )
{
prev->next = node->next;
FreeNode( node );
priorityQueues[level] = dummy.next;
return;
}
prev = searchNode;
}
ASSERT( 0 );
}
static void ReduceNode( quantnode_t *node, int nodeLevel )
{
int childNum;
if( node->timesUsed == 0 )
{
numColorsUsed++;
}
for( childNum = 0; childNum < 8; childNum++ )
{
quantnode_t *child;
child = node->children[childNum];
if( child )
{
node->cumulativeColor[0] += child->cumulativeColor[0];
node->cumulativeColor[1] += child->cumulativeColor[1];
node->cumulativeColor[2] += child->cumulativeColor[2];
node->timesUsed += child->timesUsed;
RemoveFromPriorityQueue( nodeLevel + 1, child );
numColorsUsed--;
node->children[childNum] = NULL;
}
}
if( !node->isLeaf )
{
node->next = priorityQueues[nodeLevel];
priorityQueues[nodeLevel] = node;
}
node->isLeaf = true;
}
static void ReduceTree( void )
{
int i;
for( i = 8; i > 0; i-- )
{
if( priorityQueues[i] )
{
ReduceNode( priorityQueues[i]->parent, i - 1 );
return;
}
}
ASSERT( 0 );
}
static void AddToNode( quantnode_t *node, int depth, byte *color )
{
node->timesUsed++;
node->cumulativeColor[0] += ( 1.0f / 255.0f ) * color[0];
node->cumulativeColor[1] += ( 1.0f / 255.0f ) * color[1];
node->cumulativeColor[2] += ( 1.0f / 255.0f ) * color[2];
node->isLeaf = true;
// insert into priority queue if not already there.
if( node->timesUsed == 1 )
{
node->next = priorityQueues[depth];
priorityQueues[depth] = node;
numColorsUsed++;
}
}
static void Insert( quantnode_t *node, quantnode_t *parent, int depth, uint r, uint g, uint b )
{
int childNum;
if( depth == 8 || node->isLeaf )
{
if( numColorsUsed < desiredNumColors )
{
// just add it and go since we have pal entries to use.
AddToNode( node, depth, insertColor );
}
else
{
// make space and try again.
while( numColorsUsed >= desiredNumColors )
{
ReduceTree();
}
Insert( root, NULL, 0, insertColor[0], insertColor[1], insertColor[2] );
}
return;
}
// figure out which child to go to.
childNum = (( r & ( 1 << 7 )) >> 5 ) | (( g & ( 1 << 7 )) >> 6 ) | (( b & ( 1 << 7 )) >> 7 );
ASSERT( childNum >= 0 && childNum < 8 );
// does the child already exist?
if( !node->children[childNum] )
{
// before allocating anything new, make sure we have
// space for something new and start over
if( numColorsUsed >= desiredNumColors )
{
do
{
ReduceTree();
} while( numColorsUsed >= desiredNumColors );
Insert( root, NULL, 0, insertColor[0], insertColor[1], insertColor[2] );
return;
}
node->children[childNum] = AllocNode();
node->children[childNum]->parent = node;
}
Insert( node->children[childNum], node, depth + 1, ( r << 1 ) & 0xff, ( g << 1 ) & 0xff, ( b << 1 ) & 0xff );
}
void Quantize( byte *image, int numPixels, int bytesPerPixel, int numPalEntries, byte *palette )
{
int i;
pal = palette;
desiredNumColors = numPalEntries;
root = AllocNode();
ASSERT( root );
numColorsUsed = 0;
desiredNumColors = numPalEntries;
for( i = 0; i < 9; i++ )
{
priorityQueues[i] = NULL;
}
for( i = 0; i < numPixels; i++ )
{
Q_memcpy( insertColor, &image[i*bytesPerPixel], 3 );
Insert( root, NULL, 0, (uint)insertColor[0], (uint)insertColor[1], (uint)insertColor[2] );
}
}
static void AverageColorsAndBuildPalette( quantnode_t *node )
{
vec3_t fColor;
float ooTimesUsed;
int i;
if( !node ) return;
if( node->isLeaf )
{
ooTimesUsed = 1.0f / node->timesUsed;
for( i = 0; i < 3; i++ )
{
fColor[i] = node->cumulativeColor[i] * ooTimesUsed;
if( fColor[i] > 1.0f ) fColor[i] = 1.0f;
pal[currNumPalIndices*3+i] = (byte)( fColor[i] * 255 );
}
node->paletteIndex = currNumPalIndices;
currNumPalIndices++;
return;
}
for( i = 0; i < 8; i++ )
{
AverageColorsAndBuildPalette( node->children[i] );
}
}
static void RemapPixel( quantnode_t *node, int depth, int pixel, uint r, uint g, uint b )
{
int childNum;
if( !node ) return;
if( node->isLeaf )
{
image.tempbuffer[pixel] = node->paletteIndex;
return;
}
// figure out which child to go to.
childNum = (( r & ( 1 << 7 )) >> 5 ) | (( g & ( 1 << 7 )) >> 6 ) | (( b & ( 1 << 7 )) >> 7 );
ASSERT( childNum >= 0 && childNum < 8 );
RemapPixel( node->children[childNum], depth + 1, pixel, ( r << 1 ) & 0xff, ( g << 1 ) & 0xff, ( b << 1 ) & 0xff );
}
static void MapImageToPalette( byte *in, int numPixels, int bpp )
{
int i;
for( i = 0; i < numPixels; i++ )
{
RemapPixel( root, 0, i, (uint)in[i*bpp+0], (uint)in[i*bpp+1], (uint)in[i*bpp+2] );
}
}
// returns the actual number of palette entries.
rgbdata_t *Image_Quantize( rgbdata_t *pic )
{
byte palette[768];
int bpp;
// quick case to reject unneeded conversions
if( pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32 )
return pic;
// get image description
switch( pic->type )
{
case PF_RGB_24:
case PF_BGR_24:
bpp = 3;
break;
case PF_RGBA_32:
case PF_BGRA_32:
bpp = 4;
break;
default:
MsgDev( D_ERROR, "Image_Quantize: unsupported image type %s\n", PFDesc[pic->type].name );
return pic;
}
Image_CopyParms( pic );
image.size = image.width * image.height;
image.palette = palette;
image.ptr = 0;
// allocate 8-bit buffer
image.tempbuffer = Mem_Realloc( host.imagepool, image.tempbuffer, image.size );
InitNodeAllocation();
Quantize( pic->buffer, image.size, bpp, 256, palette );
currNumPalIndices = 0;
AverageColorsAndBuildPalette( root );
MapImageToPalette( pic->buffer, image.size, bpp );
pic->buffer = Mem_Realloc( host.imagepool, pic->buffer, image.size );
Q_memcpy( pic->buffer, image.tempbuffer, image.size );
pic->palette = Mem_Alloc( host.imagepool, sizeof( palette ));
Q_memcpy( pic->palette, palette, sizeof( palette ));
pic->type = PF_INDEXED_24;
pic->size = image.size;
image.palette = NULL;
return pic;
}