Main Page | Alphabetical List | Compound List | File List | Compound Members | File Members

palettize.c

Go to the documentation of this file.
00001 /****************************************************************************************/
00002 /*  Palettize                                                                           */
00003 /*                                                                                      */
00004 /*  Author: Charles Bloom                                                               */
00005 /*  Description:  Palettize-ing code                                                    */
00006 /*                                                                                      */
00007 /*  The contents of this file are subject to the Genesis3D Public License               */
00008 /*  Version 1.01 (the "License"); you may not use this file except in                   */
00009 /*  compliance with the License. You may obtain a copy of the License at                */
00010 /*  http://www.genesis3d.com                                                            */
00011 /*                                                                                      */
00012 /*  Software distributed under the License is distributed on an "AS IS"                 */
00013 /*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */
00014 /*  the License for the specific language governing rights and limitations              */
00015 /*  under the License.                                                                  */
00016 /*                                                                                      */
00017 /*  The Original Code is Genesis3D, released March 25, 1999.                            */
00018 /*Genesis3D Version 1.1 released November 15, 1999                            */
00019 /*  Copyright (C) 1999 WildTangent, Inc. All Rights Reserved           */
00020 /*                                                                                      */
00021 /****************************************************************************************/
00022 
00023 /*********
00024 
00025 our colors are referred to as "RGB" triples, but are actually typically YUV
00026 
00027 ------
00028 
00029 can palettize a 256x256 bitmap in less than 0.1 seconds
00030 
00031 ------
00032 
00033 we palettize ("inverse colormap") using an octree lookup system
00034 
00035 <> do we need to be able to palettize to RGBA ??
00036 
00037 **********/
00038 
00039 #include "palettize.h"
00040 #include <stdlib.h>
00041 #include <assert.h>
00042 #include "ram.h"
00043 #include "mempool.h"
00044 
00045 #ifdef _TSC
00046 #pragma message("palettize using TSC")
00047 #include "tsc.h"
00048 #endif
00049 
00050 /*******/
00051 
00052 #define new(type)               geRam_AllocateClear(sizeof(type))
00053 #define allocate(ptr)   ptr = geRam_AllocateClear(sizeof(*ptr))
00054 #define clear(ptr)              memset(ptr,0,sizeof(*ptr))
00055 #define destroy(ptr)    if ( ptr ) { geRam_Free(ptr); (ptr) = NULL; } else
00056 
00057 /*******/
00058 
00059 typedef struct palInfo palInfo;
00060 
00061 int __inline    closestPalInlineRGB(int R,int G,int B,palInfo *pi);
00062 int                             closestPal(int R,int G,int B,palInfo *pi);
00063 palInfo *               closestPalInit(uint8 * palette);
00064 void                    closestPalFree(palInfo *info);
00065 
00066 /******/
00067 
00068 geBoolean palettizePlane(const  geBitmap_Info * SrcInfo,const   void * SrcBits,
00069                                                                 geBitmap_Info * DstInfo,                void * DstBits,
00070                                                                 int SizeX,int SizeY)
00071 {
00072 palInfo *palInfo;
00073 int x,y,xtra,bpp;
00074 gePixelFormat Format;
00075 int R,G,B,A;
00076 uint8 palette[768],*pSrc,*pDst;
00077 
00078         assert( SrcInfo && SrcBits );
00079         assert( DstInfo && DstBits );
00080 
00081         assert( DstInfo->Format == GE_PIXELFORMAT_8BIT_PAL );
00082         assert( gePixelFormat_IsRaw(SrcInfo->Format) );
00083 
00084         if ( ! DstInfo->Palette )
00085                 return GE_FALSE;
00086 
00087         if ( ! geBitmap_Palette_GetData(DstInfo->Palette,palette,GE_PIXELFORMAT_24BIT_RGB,256) )
00088                 return GE_FALSE;
00089 
00090 #ifdef _TSC
00091         pushTSC();
00092 #endif
00093 
00094         // rgbPlane is (planeLen*3) bytes
00095         // palette is 768 bytes
00096 
00097         palInfo = closestPalInit(palette);
00098         if ( ! palInfo ) return GE_FALSE;
00099 
00100         Format = SrcInfo->Format;
00101         bpp = gePixelFormat_BytesPerPel(Format);
00102         xtra = (SrcInfo->Stride - SizeX) * bpp;
00103         pSrc = (uint8 *)SrcBits;
00104         pDst = DstBits;
00105 
00106         if ( DstInfo->HasColorKey )
00107         {
00108         int DstCK;
00109         const gePixelFormat_Operations * ops;
00110                 ops = gePixelFormat_GetOperations(Format);
00111                 assert(ops);
00112                 DstCK = DstInfo->ColorKey;
00113 
00114                 if ( gePixelFormat_HasAlpha(Format) )   
00115                 {
00116                 gePixelFormat_ColorGetter GetColor;
00117                         GetColor = ops->GetColor;
00118                         for(y=SizeY;y--;)
00119                         {
00120                                 for(x=SizeX;x--;)
00121                                 {
00122                                         GetColor(&pSrc,&R,&G,&B,&A);
00123                                         if ( A < 128 )
00124                                         {
00125                                                 *pDst++ = DstCK;
00126                                         }
00127                                         else
00128                                         {
00129                                                 *pDst = closestPalInlineRGB(R,G,B,palInfo);
00130                                                 if ( *pDst == DstCK ) // {} this is really poor color-key avoidance!
00131                                                         *pDst ^= 1;
00132                                                 pDst++;
00133                                         }
00134                                 }
00135                                 pSrc += xtra;
00136                                 pDst += DstInfo->Stride - SizeX;
00137                         }
00138                 }
00139                 else if ( SrcInfo->HasColorKey )
00140                 {
00141                 uint32 SrcCK,Pixel;
00142                 gePixelFormat_PixelGetter GetPixel;
00143                 gePixelFormat_Decomposer DecomposePixel;
00144                         DecomposePixel = ops->DecomposePixel;
00145                         GetPixel = ops->GetPixel;
00146 
00147                         SrcCK = SrcInfo->ColorKey;
00148 
00149                         for(y=SizeY;y--;)
00150                         {
00151                                 for(x=SizeX;x--;)
00152                                 {
00153                                         Pixel = GetPixel(&pSrc);
00154                                         if ( Pixel == SrcCK )
00155                                         {
00156                                                 *pDst++ = DstCK;
00157                                         }
00158                                         else
00159                                         {
00160                                                 DecomposePixel(Pixel,&R,&G,&B,&A);
00161                                                 *pDst = closestPalInlineRGB(R,G,B,palInfo);
00162                                                 if ( *pDst == DstCK ) // {} this is really poor color-key avoidance!
00163                                                         *pDst ^= 1;
00164                                                 pDst++;
00165                                         }
00166                                 }
00167                                 pSrc += xtra;
00168                                 pDst += DstInfo->Stride - SizeX;
00169                         }
00170                 }
00171                 else
00172                 {
00173                 gePixelFormat_ColorGetter GetColor;
00174                         GetColor = ops->GetColor;
00175 
00176                         for(y=SizeY;y--;)
00177                         {
00178                                 for(x=SizeX;x--;)
00179                                 {
00180                                         GetColor(&pSrc,&R,&G,&B,&A);
00181                                         *pDst = closestPalInlineRGB(R,G,B,palInfo);
00182                                         if ( *pDst == DstCK ) // {} this is really poor color-key avoidance!
00183                                                 *pDst ^= 1;
00184                                         pDst++;
00185                                 }
00186                                 pSrc += xtra;
00187                                 pDst += DstInfo->Stride - SizeX;
00188                         }
00189                 }
00190         }
00191         else
00192         {
00193                 // dst does not have CK, and can't have alpha in this universe, so ignore src colorkey
00194         #if 0 // these special cases just avoid a functional-call overhead
00195                 if ( Format == GE_PIXELFORMAT_24BIT_RGB )
00196                 {
00197                         for(y=SizeY;y--;)
00198                         {
00199                                 for(x=SizeX;x--;)
00200                                 {
00201                                         R = *pSrc++; G = *pSrc++; B = *pSrc++;
00202                                         *pDst++ = closestPalInlineRGB(R,G,B,palInfo);
00203                                 }
00204                                 pSrc += xtra;
00205                                 pDst += DstInfo->Stride - SizeX;
00206                         }
00207                 }
00208                 else if ( Format == GE_PIXELFORMAT_24BIT_BGR )
00209                 {
00210                         for(y=SizeY;y--;)
00211                         {
00212                                 for(x=SizeX;x--;)
00213                                 {
00214                                         B = *pSrc++; G = *pSrc++; R = *pSrc++;
00215                                         *pDst++ = closestPalInlineRGB(R,G,B,palInfo);
00216                                 }
00217                                 pSrc += xtra;
00218                                 pDst += DstInfo->Stride - SizeX;
00219                         }
00220                 }
00221                 else
00222         #endif
00223                 {
00224                 const gePixelFormat_Operations * ops;
00225                 gePixelFormat_ColorGetter GetColor;
00226                         ops = gePixelFormat_GetOperations(Format);
00227                         assert(ops);
00228                         GetColor = ops->GetColor;
00229                         assert(GetColor);
00230                         for(y=SizeY;y--;)
00231                         {
00232                                 for(x=SizeX;x--;)
00233                                 {
00234                                         GetColor(&pSrc,&R,&G,&B,&A);
00235                                         *pDst++ = closestPalInlineRGB(R,G,B,palInfo);
00236                                 }
00237                                 pSrc += xtra;
00238                                 pDst += DstInfo->Stride - SizeX;
00239                         }
00240                 }
00241         }
00242 
00243 #ifdef _TSC
00244         showPopTSC("palettize");
00245 #endif
00246 
00247         closestPalFree(palInfo);
00248         
00249 return GE_TRUE;
00250 }
00251 
00252 /***************
00253 **
00254 *
00255 
00256 Build an OctTree containing all the palette entries; the RGB value
00257 is the index into the tree, the value at the leaf is a palette index.  All null children are then set
00258 to point to their closest neighbor.  It has a maximum depth of 8.
00259 
00260 To find a palette entry, you take your RGB and just keep stepping in; in fact, it's quite trivial.
00261 on an image of B bytes and a palette of P entries, this method is O(B+P)
00262 
00263 Find the right neighbor for null children is a very difficult algorithm.  I punt and
00264 leave them null; when we find a null in descent, we do a hash-assisted search to find
00265 the right pal entry, then add this color & pal entry to the octree for future use.
00266 
00267 we store palette entries in the octree as (palVal+1) so that we can use 0 to mean "not assigned"
00268 
00269 per-pixel time : 5e-7   (found in octree lookup)
00270 per-color time : 7e-6   (not in octree time)
00271 
00272 total_seconds = (5e-7)*(num_pels + palettize_size) + 
00273         (3e-8)*(num_actual_colors - palettize_size)*(palettize_size)
00274 
00275         (coder=bitplane,transform=L97)
00276 
00277 stop-rate 4 , PSNR on :
00278 brute-force 
00279         pal1 ; 33.29
00280         pal2 : 37.69
00281         pal3 : 33.69
00282         pal4 : 44.69
00283 OctTree without "expandNulls"   ("fast")
00284         pal1 ; 25.73
00285         pal2 : 32.50
00286         pal3 : 27.84
00287         pal4 : 28.07
00288 OctTree with brute-force "expandNulls"
00289         pal1 ; 32.53
00290         pal2 : 37.09
00291         pal3 : 33.27
00292         pal4 : 33.50
00293 OctTree with brute-force on null
00294         pal1 ; 33.15
00295         pal2 : 37.71
00296         pal3 : 33.65
00297         pal4 : 35.05
00298 *
00299 **
00300  *****************/
00301 
00302 #define QUANT_BITS      (4)
00303 #define QUANT_SHIFT     (8-QUANT_BITS)
00304 #define QUANT_ROUND     (1<<(QUANT_SHIFT-1))
00305 #define HASH_BITS       (QUANT_BITS*3)
00306 #define HASH_SIZE       (1<<HASH_BITS)
00307 #define HASH(R,G,B)     ( (((R)>>QUANT_SHIFT)<<(QUANT_BITS+QUANT_BITS)) + (((G)>>QUANT_SHIFT)<<(QUANT_BITS)) + (((B)>>QUANT_SHIFT)))
00308 #define HASHROUNDED(R,G,B)      ( (((R+QUANT_ROUND)>>QUANT_SHIFT)<<(QUANT_BITS+QUANT_BITS)) + (((G+QUANT_ROUND)>>QUANT_SHIFT)<<(QUANT_BITS)) + (((B+QUANT_ROUND)>>QUANT_SHIFT)))
00309 
00310 typedef struct octNode octNode;
00311 struct octNode 
00312 {
00313         octNode * kids[8];
00314         octNode * parent;
00315 };
00316 
00317 typedef struct hashNode 
00318 {
00319         struct hashNode *next;
00320         int R,G,B,pal;
00321 } hashNode;
00322 
00323 struct palInfo 
00324 {
00325         uint8 *palette;
00326         octNode *root;
00327         hashNode * hash[HASH_SIZE+1];
00328 };
00329 
00330 // internal protos:
00331 
00332 int colorDistance(uint8 *ca,uint8 *cb);
00333 int findClosestPalBrute(int R,int G,int B,palInfo *pi);
00334 void addOctNode(octNode *root,int R,int G,int B,int palVal);
00335 void addHash(palInfo *pi,int R,int G,int B,int palVal,int hash);
00336 
00337 #define RGBbits(R,G,B,bits) (((((R)>>(bits))&1)<<2) + ((((G)>>(bits))&1)<<1) + (((B)>>((bits)))&1))
00338 
00339 static MemPool * octNodePool = NULL;
00340 static MemPool * hashNodePool = NULL;
00341 static int PoolRefs = 0;
00342 
00343 void Palettize_Start(void)
00344 {
00345         if ( PoolRefs == 0 )
00346         {
00347                 // we init with 256 octnodes, then add one for each unique color
00348                 octNodePool = MemPool_Create(sizeof(octNode),1024,1024);
00349                 assert(octNodePool);
00350                 hashNodePool = MemPool_Create(sizeof(hashNode),1024,1024);
00351                 assert(hashNodePool);
00352         }
00353         PoolRefs ++;
00354 }
00355 
00356 void Palettize_Stop(void)
00357 {
00358         PoolRefs --;
00359         if ( PoolRefs == 0 )
00360         {
00361                 MemPool_Destroy(&octNodePool);
00362                 MemPool_Destroy(&hashNodePool);
00363         }
00364 }
00365 
00366 /********************/
00367 
00368 palInfo * closestPalInit(uint8 * palette)
00369 {
00370 palInfo *pi;
00371 int i;
00372 
00373         i = HASH_SIZE;
00374 
00375         if ( (pi = new(palInfo)) == NULL )
00376                 return NULL;
00377 
00378         pi->palette = palette;
00379 
00380         pi->root = MemPool_GetHunk(octNodePool);
00381         assert(pi->root);
00382 
00383         for(i=0;i<256;i++) 
00384         {
00385                 int R,G,B;
00386                 R = palette[3*i]; G = palette[3*i+1]; B = palette[3*i+2];
00387                 addOctNode(pi->root,R,G,B,i);
00388                 addHash(pi,R,G,B,i,HASH(R,G,B));
00389         }
00390 
00391 return pi;
00392 }
00393 
00394 int findClosestPal(int R,int G,int B,palInfo *pi)
00395 {
00396 hashNode *node;
00397 int hash,d,bestD,bestP;
00398 
00399         hash = HASHROUNDED(R,G,B);
00400         if ( hash > HASH_SIZE ) hash = HASH_SIZE;
00401 
00402         node = pi->hash[ hash ];
00403         if ( ! node ) 
00404         {
00405                 bestP = findClosestPalBrute(R,G,B,pi);
00406 #if 1
00407                 // helps speed a little; depends on how common individual RGB values are
00408                 // (makes it so that if we see this exact RGB again we return bestP right away)
00409                 addOctNode(pi->root,R,G,B,bestP);
00410 #endif
00411 #if 0
00412                 //this could help speed, but actually makes this method approximate
00413                 node = MemPool_GetHunk(hashNodePool);
00414                 assert(node);
00415                 node->next = pi->hash[hash];
00416                 pi->hash[hash] = node;
00417                 node->R = R;
00418                 node->G = G;
00419                 node->B = B;
00420                 node->pal = bestP;
00421 #endif
00422                 return bestP;
00423         }
00424 
00425         bestD = 99999999;       bestP = node->pal;
00426         while(node) 
00427         {
00428                 d = (R - node->R)*(R - node->R) + (G - node->G)*(G - node->G) + (B - node->B)*(B - node->B);
00429                 if ( d < bestD ) 
00430                 {
00431                         bestD = d;
00432                         bestP = node->pal;
00433                 }
00434                 node = node->next;
00435         }
00436 
00437 #if 1
00438         // <> ?
00439         // helps speed a little; depends on how common individual RGB values are
00440         // (makes it so that if we see this exact RGB again we return bestP right away)
00441         addOctNode(pi->root,R,G,B,bestP);
00442 #endif
00443 
00444         return bestP;
00445 }
00446 
00447 #define doStep(bits)    do { kid = (node)->kids[ RGBbits(R,G,B,bits) ]; \
00448                                 if ( kid ) node = kid; else return findClosestPal(R,G,B,pi); } while(0)
00449 
00450 #define doSteps()       do { node = pi->root; doStep(7); doStep(6); doStep(5); doStep(4); doStep(3); doStep(2); doStep(1); doStep(0); } while(0)
00451 
00452 int __inline closestPalInlineRGB(int R,int G,int B,palInfo *pi)
00453 {
00454 octNode *node,*kid;
00455 
00456         doSteps();
00457 
00458 return ((int)node)-1;
00459 }
00460 
00461 int closestPal(int R,int G,int B,palInfo *pi)
00462 {
00463 octNode *node,*kid;
00464 
00465         doSteps();
00466 
00467         assert( ((int)node) <= 256 && ((int)node) > 0 );
00468 
00469 return ((int)node)-1;
00470 }
00471 
00472 void closestPalFree(palInfo *pi)
00473 {
00474 
00475         assert(pi);
00476         
00477         MemPool_Reset(octNodePool);
00478         MemPool_Reset(hashNodePool);
00479 
00480         destroy(pi);
00481 }
00482 
00483 
00484 int findClosestPalBrute(int R,int G,int B,palInfo *pi)
00485 {
00486 int d,p;
00487 int bestD,bestP;
00488 uint8 * pal;
00489 uint8 color[3];
00490 
00491         // now do a brute-force best-pal search to find the best pal entry
00492 
00493         color[0] = R;   color[1] = G;   color[2] = B;
00494         pal = pi->palette;
00495         bestD = colorDistance(color,pal);
00496         bestP = 0;
00497         for(p=1;p<256;p++)
00498         {
00499                 pal += 3;
00500                 d = colorDistance(color,pal);
00501                 if ( d < bestD ) 
00502                 {
00503                         bestD = d;
00504                         bestP = p;
00505                 }
00506         }
00507 return bestP;
00508 }
00509 
00510 int colorDistance(uint8 *ca,uint8 *cb)
00511 {
00512 int d,x;
00513         d = 0;
00514         x = ca[0] - cb[0];
00515         d += x*x;
00516         x = ca[1] - cb[1];
00517         d += x*x;
00518         x = ca[2] - cb[2];
00519         d += x*x;
00520 return d;
00521 }
00522 
00523 void addOctNode(octNode *root,int R,int G,int B,int palVal)
00524 {
00525 int idx;
00526 int bits;
00527 octNode *node = root;
00528 
00529         for(bits=7;bits>0;bits--) 
00530         {
00531                 idx = RGBbits(R,G,B,bits);
00532                 if ( ! node->kids[idx] ) 
00533                 {
00534                         node->kids[idx] = MemPool_GetHunk(octNodePool);
00535                         node->kids[idx]->parent = node;
00536                 }
00537                 node = node->kids[idx];
00538         }
00539         idx = RGBbits(R,G,B,0);
00540         node->kids[idx] = (octNode *)(palVal+1);
00541 }
00542 
00543 void addHash(palInfo *pi,int R,int G,int B,int palVal,int hash)
00544 {
00545 hashNode *node;
00546 int i,h;
00547 
00548         for(i=0;i<8;i++) 
00549         {
00550                 h = hash + (i&1) + (((i>>1)&1)<<QUANT_BITS) + ((i>>2)<<(QUANT_BITS+QUANT_BITS));
00551                 if ( h <= HASH_SIZE ) 
00552                 {
00553                         node = MemPool_GetHunk(hashNodePool);
00554                         assert(node);
00555                         node->next = pi->hash[h];
00556                         pi->hash[h] = node;
00557                         node->R = R;
00558                         node->G = G;
00559                         node->B = B;
00560                         node->pal = palVal;
00561                 }
00562         }
00563 }

Generated on Tue Sep 30 12:36:03 2003 for GTestAndEngine by doxygen 1.3.2