00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
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
00095
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 )
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 )
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 )
00183 *pDst ^= 1;
00184 pDst++;
00185 }
00186 pSrc += xtra;
00187 pDst += DstInfo->Stride - SizeX;
00188 }
00189 }
00190 }
00191 else
00192 {
00193
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
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
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
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
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
00408
00409 addOctNode(pi->root,R,G,B,bestP);
00410 #endif
00411 #if 0
00412
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
00440
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
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 }