00001
00007 #include <stdio.h>
00008 #include "ctl/ctldef.h"
00009 #include "bmp.h"
00010
00011 #ifndef CTL_UNIT
00012
00020 void BMI_OPC( OPC* opc, const BITMAPINFO* bmi, void* bits )
00021 {
00022 OPC* out = (opc);
00023 const BITMAPINFO* bmin = (bmi);
00024 out->origin = (uint8*)BMI_Origin(bmin,bits);
00025 out->pitch = BMI_LineOffset(bmin);
00026 out->clip.left = out->clip.top = 0;
00027 out->clip.right = BMI_Wide(bmin);
00028 out->clip.bottom = BMI_High(bmin);
00029 }
00030
00031
00032
00033
00034 static unsigned ddRGBQUAD( const RGBQUAD* c1, const RGBQUAD* c2 )
00035 {
00036 int diffr = c1->rgbRed - c2->rgbRed;
00037 int diffg = c1->rgbGreen - c2->rgbGreen;
00038 int diffb = c1->rgbBlue - c2->rgbBlue;
00039 return (diffr*diffr)+(diffg*diffg)+(diffb*diffb);
00040 }
00041
00045 int RGBQUAD_Nearest( const RGBQUAD* clut, size_t count, const RGBQUAD* color )
00046 {
00047 const RGBQUAD* best = clut;
00048 unsigned bestDistance = ddRGBQUAD( best, color );
00049 unsigned score;
00050 pointer_foreach_const(RGBQUAD,clut+1,count-1, curr )
00051 {
00052 score = ddRGBQUAD( curr, color );
00053 if( score < bestDistance )
00054 {
00055 best = curr;
00056 bestDistance = score;
00057 if( !score )
00058 break;
00059 }
00060 }
00061 return best ? best - clut : 0;
00062 }
00063
00064
00065 static int lsb( uint32 val )
00066 {
00067 int bit = 0;
00068 while( val && !(val&1) )
00069 {
00070 val>>=1;
00071 bit++;
00072 }
00073 return bit;
00074 }
00075
00076
00077 static int nbits( uint32 val )
00078 {
00079 int bit = 0;
00080 while( val && !(val&1) )
00081 val>>=1;
00082 while( val&1 )
00083 {
00084 val>>=1;
00085 bit++;
00086 }
00087 return bit;
00088 }
00089
00090
00100 bool BMI_ReadRow( RGBQUAD* pixel, const BITMAPINFO* bmi, const void* bits, int row )
00101 {
00102
00103 assert(bmi);
00104 assert(bits);
00105 if( row < 0 || row >= BMI_High(bmi) || (bmi->bmiHeader.biCompression != BI_RGB && bmi->bmiHeader.biCompression != BI_BITFIELDS && bmi->bmiHeader.biCompression != BI_RLE4 && bmi->bmiHeader.biCompression != BI_RLE8) )
00106 {
00107 memset( pixel, 0, sizeof(RGBQUAD)*BMI_Wide(bmi) );
00108 return false;
00109 }
00110 switch( bmi->bmiHeader.biBitCount )
00111 {
00112 case 1:
00113 {
00114 const RGBQUAD* colors = BMI_ColorData(bmi);
00115 const uint8* pixels = (const uint8*)BMI_LinePtr(bmi,bits,row);
00116 int wordlen = BMI_Wide(bmi);
00117 while( wordlen )
00118 {
00119 uint8 bit = 0x80;
00120 while( bit && wordlen )
00121 {
00122 *pixel++ = colors[0 != (*pixels & bit)];
00123 wordlen--;
00124 bit >>= 1;
00125 }
00126 pixels++;
00127 }
00128 }
00129 return true;
00130
00131 case 4:
00132 {
00133 const RGBQUAD* colors = BMI_ColorData(bmi);
00134 const uint8* pixels = (const uint8*)BMI_LinePtr(bmi,bits,row);
00135 int wordlen = BMI_Wide(bmi);
00136 int curr;
00137 for( curr = 0; curr < wordlen; ++curr )
00138 {
00139 if( curr&1 )
00140 {
00141 *pixel++ = colors[*pixels&0xf];
00142 pixels++;
00143 }
00144 else
00145 {
00146 *pixel++ = colors[(*pixels>>4)&0xf];
00147 }
00148 }
00149 }
00150 return true;
00151
00152 case 8:
00153 {
00154 const RGBQUAD* colors = BMI_ColorData(bmi);
00155 const uint8* pixels = (const uint8*)BMI_LinePtr(bmi,bits,row);
00156 int wordlen = BMI_Wide(bmi);
00157 while( wordlen-- )
00158 *pixel++ = colors[*pixels++];
00159 }
00160 return true;
00161
00162 case 16:
00163 if( bmi->bmiHeader.biCompression == BI_BITFIELDS )
00164 {
00165 const uint32* masks = (const uint32*)BMI_ColorData(bmi);
00166 uint16 mRed = (uint16)masks[0];
00167 uint16 mGreen = (uint16)masks[1];
00168 uint16 mBlue = (uint16)masks[2];
00169 int bRed = lsb(mRed);
00170 int bGreen = lsb(mGreen);
00171 int bBlue = lsb(mBlue);
00172 int nRed = nbits( mRed );
00173 int nGreen = nbits( mGreen );
00174 int nBlue = nbits( mBlue );
00175 const uint16* pixels = (const uint16*)BMI_LinePtr(bmi,bits,row);
00176 int wordlen = BMI_Wide(bmi);
00177 while( wordlen-- )
00178 {
00179 pixel->rgbBlue = (uint8)(((mBlue & *pixels) >> bBlue) << (8-nBlue));
00180 pixel->rgbGreen = (uint8)(((mGreen & *pixels) >> bGreen) << (8-nGreen));
00181 pixel->rgbRed = (uint8)(((mRed & *pixels) >> bRed) << (8-nRed));
00182 pixel->rgbReserved = 0;
00183 pixel++;
00184 pixels++;
00185 }
00186 }
00187 else
00188 {
00189 const uint16* pixels = (const uint16*)BMI_LinePtr(bmi,bits,row);
00190 int wordlen = BMI_Wide(bmi);
00191 while( wordlen-- )
00192 {
00193 pixel->rgbBlue = (uint8)((*pixels & 0x1f) << 3);
00194 pixel->rgbGreen = (uint8)(((*pixels >> 5) & 0x1f) << 3);
00195 pixel->rgbRed = (uint8)(((*pixels >> 10) & 0x1f) << 3);
00196 pixel->rgbReserved = 0;
00197 pixels++;
00198 pixel++;
00199 }
00200 }
00201 return true;
00202
00203 case 24:
00204 {
00205 const uint8* pixels = (const uint8*)BMI_LinePtr(bmi,bits,row);
00206 int wordlen = BMI_Wide(bmi);
00207 while( wordlen-- )
00208 {
00209 pixel->rgbBlue = *pixels++;
00210 pixel->rgbGreen = *pixels++;
00211 pixel->rgbRed = *pixels++;
00212 pixel->rgbReserved = 0;
00213 pixel++;
00214 }
00215 }
00216 return true;
00217
00218 case 32:
00219 if( bmi->bmiHeader.biCompression == BI_BITFIELDS )
00220 {
00221 const uint32* masks = (const uint32*)BMI_ColorData(bmi);
00222 uint32 mRed = (uint32)masks[0];
00223 uint32 mGreen = (uint32)masks[1];
00224 uint32 mBlue = (uint32)masks[2];
00225 int bRed = lsb(mRed);
00226 int bGreen = lsb(mGreen);
00227 int bBlue = lsb(mBlue);
00228 int nRed = nbits( mRed );
00229 int nGreen = nbits( mGreen );
00230 int nBlue = nbits( mBlue );
00231 const uint32* pixels = (const uint32*)BMI_LinePtr(bmi,bits,row);
00232 int wordlen = BMI_Wide(bmi);
00233 while( wordlen-- )
00234 {
00235 pixel->rgbBlue = (uint8)(((mBlue & *pixels) >> bBlue) << (8-nBlue));
00236 pixel->rgbGreen = (uint8)(((mGreen & *pixels) >> bGreen) << (8-nGreen));
00237 pixel->rgbRed = (uint8)(((mRed & *pixels) >> bRed) << (8-nRed));
00238 pixel->rgbReserved = 0;
00239 pixels++;
00240 pixel++;
00241 }
00242 }
00243 else
00244 {
00245 const uint8* pixels = (const uint8*)BMI_LinePtr(bmi,bits,row);
00246 int wordlen = BMI_Wide(bmi);
00247 while( wordlen-- )
00248 {
00249 pixel->rgbBlue = *pixels++;
00250 pixel->rgbGreen = *pixels++;
00251 pixel->rgbRed = *pixels++;
00252 pixel->rgbReserved = 0;
00253 pixel++;
00254 pixels++;
00255 }
00256 }
00257 return true;
00258
00259 default:
00260 break;
00261 }
00262 return false;
00263 }
00264
00265
00275 bool BMI_WriteRow( const BITMAPINFO* bmi, void* bits, const RGBQUAD* pixel, int row )
00276 {
00277
00278 assert(bmi);
00279 assert(bits);
00280 if( row < 0 || row >= BMI_High(bmi) || (bmi->bmiHeader.biCompression != BI_RGB && bmi->bmiHeader.biCompression != BI_BITFIELDS) )
00281 {
00282 return false;
00283 }
00284 switch( bmi->bmiHeader.biBitCount )
00285 {
00286 case 1:
00287 {
00288
00289 const RGBQUAD* colors = BMI_ColorData(bmi);
00290 uint8* pixels = (uint8*)BMI_LinePtr(bmi,bits,row);
00291 int wordlen = BMI_Wide(bmi);
00292 while( wordlen )
00293 {
00294 uint8 bit = 0x80;
00295 *pixels = 0;
00296 while( bit && wordlen )
00297 {
00298 if( RGBQUAD_Nearest( colors, 2, pixel++ ) )
00299 *pixels |= bit;
00300 bit>>=1;
00301 wordlen--;
00302 }
00303 pixels++;
00304 }
00305 }
00306 return true;
00307
00308 case 4:
00309 {
00310 const RGBQUAD* colors = BMI_ColorData(bmi);
00311 uint8* pixels = (uint8*)BMI_LinePtr(bmi,bits,row);
00312 int wordlen = BMI_Wide(bmi);
00313 int curr;
00314 for( curr = 0; curr < wordlen; ++curr )
00315 {
00316 unsigned index = (unsigned)RGBQUAD_Nearest( colors, 16, pixel++ );
00317 if( wordlen & 1 )
00318 *pixels++ = (uint8)index;
00319 else
00320 *pixels |= (uint8)(index<<4);
00321 }
00322 }
00323 return true;
00324
00325 case 8:
00326 {
00327
00328 const RGBQUAD* colors = BMI_ColorData(bmi);
00329 uint8* pixels = (uint8*)BMI_LinePtr(bmi,bits,row);
00330 int wordlen = BMI_Wide(bmi);
00331 while( wordlen-- )
00332 *pixels++ = (uint8)RGBQUAD_Nearest( colors, 256, pixel++ );
00333 }
00334 return true;
00335
00336 case 16:
00337 if( bmi->bmiHeader.biCompression == BI_BITFIELDS )
00338 {
00339
00340 const uint32* masks = (const uint32*)BMI_ColorData(bmi);
00341 uint16 mRed = (uint16)masks[0];
00342 uint16 mGreen = (uint16)masks[1];
00343 uint16 mBlue = (uint16)masks[2];
00344 int bRed = lsb(mRed);
00345 int bGreen = lsb(mGreen);
00346 int bBlue = lsb(mBlue);
00347 int nRed = nbits( mRed );
00348 int nGreen = nbits( mGreen );
00349 int nBlue = nbits( mBlue );
00350 uint16* pixels = (uint16*)BMI_LinePtr(bmi,bits,row);
00351 int wordlen = BMI_Wide(bmi);
00352 while( wordlen-- )
00353 {
00354 *pixels = 0;
00355 *pixels |= (((uint16)pixel->rgbBlue & ((1<<nBlue)-1)) >> (8-nBlue)) << bBlue;
00356 *pixels |= (((uint16)pixel->rgbGreen & ((1<<nGreen)-1)) >> (8-nGreen)) << bGreen;
00357 *pixels |= (((uint16)pixel->rgbRed & ((1<<nRed)-1)) >> (8-nRed)) << bRed;
00358 pixel++;
00359 pixels++;
00360 }
00361 }
00362 else
00363 {
00364
00365 uint16* pixels = (uint16*)BMI_LinePtr(bmi,bits,row);
00366 int wordlen = BMI_Wide(bmi);
00367 while( wordlen-- )
00368 {
00369 *pixels = 0;
00370 *pixels |= ((uint16)pixel->rgbBlue & 0xf8) >> 3;
00371 *pixels |= ((uint16)pixel->rgbGreen & 0xf8) << 2;
00372 *pixels |= ((uint16)pixel->rgbRed & 0xf8) << 7;
00373 pixels++;
00374 pixel++;
00375 }
00376 }
00377 return true;
00378
00379 case 24:
00380 {
00381
00382 uint8* pixels = (uint8*)BMI_LinePtr(bmi,bits,row);
00383 int wordlen = BMI_Wide(bmi);
00384 while( wordlen-- )
00385 {
00386 *pixels++ = pixel->rgbBlue;
00387 *pixels++ = pixel->rgbGreen;
00388 *pixels++ = pixel->rgbRed;
00389 pixel++;
00390 }
00391 }
00392 return true;
00393
00394 case 32:
00395 if( bmi->bmiHeader.biCompression == BI_BITFIELDS )
00396 {
00397
00398 const uint32* masks = (const uint32*)BMI_ColorData(bmi);
00399 uint32 mRed = (uint32)masks[0];
00400 uint32 mGreen = (uint32)masks[1];
00401 uint32 mBlue = (uint32)masks[2];
00402 int bRed = lsb(mRed);
00403 int bGreen = lsb(mGreen);
00404 int bBlue = lsb(mBlue);
00405 int nRed = nbits( mRed );
00406 int nGreen = nbits( mGreen );
00407 int nBlue = nbits( mBlue );
00408 uint32* pixels = (uint32*)BMI_LinePtr(bmi,bits,row);
00409 int wordlen = BMI_Wide(bmi);
00410 while( wordlen-- )
00411 {
00412 *pixels = 0;
00413 *pixels |= (((uint32)pixel->rgbBlue & ((1<<nBlue)-1)) >> (8-nBlue)) << bBlue;
00414 *pixels |= (((uint32)pixel->rgbGreen & ((1<<nGreen)-1)) >> (8-nGreen)) << bGreen;
00415 *pixels |= (((uint32)pixel->rgbRed & ((1<<nRed)-1)) >> (8-nRed)) << bRed;
00416 pixel++;
00417 pixels++;
00418 }
00419 }
00420 else
00421 {
00422
00423 uint8* pixels = (uint8*)BMI_LinePtr(bmi,bits,row);
00424 int wordlen = BMI_Wide(bmi);
00425 while( wordlen-- )
00426 {
00427 *pixels++ = pixel->rgbBlue;
00428 *pixels++ = pixel->rgbGreen;
00429 *pixels++ = pixel->rgbRed;
00430 *pixels++ = 0;
00431 pixel++;
00432 }
00433 }
00434 return true;
00435
00436 default:
00437 break;
00438 }
00439 return false;
00440 }
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00462 size_t BMI_MakeClut( RGBQUAD* dstlut, size_t cClut, const BITMAPINFO* bmi, const void* bits )
00463 {
00464 BMIClut clutz;
00465 BMI_Clut_Begin( &clutz );
00466 {
00467 BMI_foreach( bmi, bits, pixel )
00468 BMI_Clut_AddColor( &clutz, pixel );
00469 }
00470 return BMI_Clut_Make( &clutz, dstlut, cClut );
00471 }
00472
00477 void BMI_Clut_Begin( BMIClut* clut )
00478 {
00479 memset( clut->cluts, 0, sizeof(clut->cluts) );
00480 }
00486 void BMI_Clut_AddColor( BMIClut* clut, const RGBQUAD* rgb )
00487 {
00488 #define CLI(RGBQUAD) ( (((RGBQUAD).rgbBlue&0xf8)>>3) | (((RGBQUAD).rgbGreen&0xf8)<<2) | (((RGBQUAD).rgbRed&0xf8)<<7) )
00489 int index = CLI(*rgb);
00490 struct BMIClutEntry* curr = clut->cluts + index;
00491 curr->rgb = *rgb;
00492 if( curr->count < type_max(uint32) )
00493 curr->count++;
00494 }
00495 static int qsclutz( const struct BMIClutEntry* c1, const struct BMIClutEntry* c2 ) { return c2->count-c1->count; }
00496 static int qspretty( const struct BMIClutEntry* c1, const struct BMIClutEntry* c2 )
00497 {
00498 int diff1,diff2,diff;
00499
00500 if( !c1->count || !c2->count )
00501 return c2->count-c1->count;
00502
00503 diff1 = c1->rgb.rgbRed == c1->rgb.rgbGreen && c1->rgb.rgbRed == c1->rgb.rgbBlue;
00504 diff2 = c2->rgb.rgbRed == c2->rgb.rgbGreen && c2->rgb.rgbRed == c2->rgb.rgbBlue;
00505 diff = diff2-diff1;
00506 if( diff )
00507 return diff;
00508
00509 diff1 = !c1->rgb.rgbRed || !c1->rgb.rgbGreen || !c1->rgb.rgbBlue;
00510 diff2 = !c2->rgb.rgbRed || !c2->rgb.rgbGreen || !c2->rgb.rgbBlue;
00511 diff = diff2-diff1;
00512 if( diff )
00513 return diff;
00514
00515 diff = c1->rgb.rgbBlue - c2->rgb.rgbBlue;
00516 if( diff )
00517 return diff;
00518 diff = c1->rgb.rgbGreen - c2->rgb.rgbGreen;
00519 if( diff )
00520 return diff;
00521 diff = c1->rgb.rgbRed - c2->rgb.rgbRed;
00522 return diff;
00523 }
00524
00525
00532 size_t BMI_Clut_Make( BMIClut* clut, RGBQUAD* dstlut, size_t cClut )
00533 {
00534 array_qsort( struct BMIClutEntry, clut->cluts, countof(clut->cluts), qsclutz );
00535 array_qsort( struct BMIClutEntry, clut->cluts, cClut, qspretty );
00536 {
00537 const struct BMIClutEntry* curr = clut->cluts;
00538 size_t remain = cClut;
00539 while( remain )
00540 {
00541 remain--;
00542 if( curr->count )
00543 *dstlut++ = (curr++)->rgb;
00544 else
00545 break;
00546 }
00547 cClut -= remain;
00548 return cClut;
00549 }
00550 }
00551
00552
00560 void BMI_Translate( const BITMAPINFO* dst, void* bdst, const BITMAPINFO* src, const void* bsrc )
00561 {
00562 RGBQUAD* colors = (RGBQUAD*)malloc( sizeof(RGBQUAD) * max(BMI_Wide(dst),BMI_Wide(src)) );
00563 int rowCurr = min( BMI_High(dst), BMI_High(src) );
00564 while( rowCurr-- )
00565 {
00566 BMI_ReadRow( colors, src, bsrc, rowCurr );
00567 BMI_WriteRow( dst, bdst, colors, rowCurr );
00568 }
00569 free(colors);
00570 }
00571
00572
00579 void* BMI_Unpack( const BITMAPINFO* bmi, const void* bits )
00580 {
00581 switch( bmi->bmiHeader.biCompression )
00582 {
00583 default:
00584 case BI_BITFIELDS:
00585 case BI_RGB:
00586 return NULL;
00587
00588 case BI_RLE4:
00589 case BI_RLE8:
00590 {
00591 size_t current = BMI_BitmapSize(bmi);
00592 size_t lineSize = BMI_LineSize(bmi);
00593 size_t needs = lineSize * BMI_High(bmi);
00594 void* ret = malloc(needs);
00595 const uint8* pSrc = (const uint8*)bits;
00596 const uint8* pEnd = pSrc + current;
00597 uint8* pDstEdge = (uint8*)ret;
00598 uint8* pDst = pDstEdge;
00599 const uint8* pdEnd = pDst + needs;
00600 while( pSrc < pEnd )
00601 {
00602 int count = *pSrc++;
00603 if( count )
00604 {
00605 uint8 fill = *pSrc++;
00606 count = min(count,(int)(pdEnd-pDst));
00607 if( bmi->bmiHeader.biCompression == BI_RLE8 )
00608 {
00609 memset( pDst, fill, count );
00610 pDst += count;
00611 }
00612 else
00613 {
00614 count>>=1;
00615 memset( pDst, fill, count );
00616 pDst += count;
00617 }
00618 }
00619 else
00620 {
00621 count = *pSrc++;
00622 if( count == 0 )
00623 {
00624 pDst = pDstEdge += lineSize;
00625 }
00626 else if( count == 1 )
00627 break;
00628 else if( count == 2 )
00629 {
00630 int x = *pSrc++;
00631 int y = *pSrc++;
00632 pDstEdge += (lineSize*y);
00633 pDst = pDstEdge + x;
00634 }
00635 else
00636 {
00637 count = min(count,(int)(pdEnd-pDst));
00638 count = min(count,(int)(pEnd-pSrc));
00639 if( bmi->bmiHeader.biCompression == BI_RLE4 )
00640 {
00641 memcpy( pDst, pSrc, count>>1 );
00642 pDst += count>>1;
00643 pSrc += count;
00644 }
00645 else
00646 {
00647 memcpy( pDst, pSrc, count );
00648 pDst += count;
00649 pSrc += count;
00650 }
00651
00652 if( bmi->bmiHeader.biCompression == BI_RLE8 )
00653 {
00654 if( count & 1 )
00655 pSrc++;
00656 }
00657 else
00658 {
00659 if( (count & 3) == 1 || (count & 3) == 2 )
00660 pSrc++;
00661 }
00662 }
00663 }
00664 }
00665 return ret;
00666 }
00667 break;
00668 }
00669 }
00670
00677 bool BMI_Save( const char* path, const BITMAPINFO* bmi, const void* bits )
00678 {
00679
00680
00681
00682
00683 FILE *fp = fopen( path, "wb" );
00684 assert( bmi );
00685 assert( bits );
00686 assert( bmi->bmiHeader.biWidth > 0 );
00687 assert( bmi->bmiHeader.biHeight );
00688 if( fp )
00689 {
00690 BITMAPFILEHEADER bmf;
00691 bmf.bfType = *((uint16 *)"BM");
00692 bmf.bfSize = sizeof(bmf) + BMI_BMI_Size(bmi) + BMI_BitmapSize(bmi);
00693 bmf.bfReserved1 = 0;
00694 bmf.bfReserved2 = 0;
00695 bmf.bfOffBits = sizeof(bmf) + BMI_BMI_Size(bmi);
00696 if( 1 != fwrite( &bmf, sizeof(bmf), 1, fp ) )
00697 {
00698 fclose( fp );
00699 return false;
00700 }
00701 {
00702
00703 BITMAPINFO *bmiScratch = BMI_Alloc();
00704 memcpy( bmiScratch, bmi, BMI_BMI_Size(bmi) );
00705 bmiScratch->bmiHeader.biHeight = abs(bmiScratch->bmiHeader.biHeight);
00706 if( 1 != fwrite( bmiScratch, BMI_BMI_Size(bmiScratch), 1, fp ) )
00707 {
00708 fclose( fp );
00709 return false;
00710 }
00711 BMI_Free(bmiScratch);
00712 }
00713 {
00714
00715 int line = BMI_High(bmi);
00716 while( line-- )
00717 {
00718 if( 1 != fwrite( BMI_LinePtr(bmi,bits,line), BMI_LineSize(bmi), 1, fp ) )
00719 break;
00720 }
00721 }
00722 fclose( fp );
00723 return true;
00724 }
00725 return false;
00726 }
00727
00728 #include "ctl/mmap.h"
00735 bool BMI_Load( const char* path, BITMAPINFO** bmi, void** bits )
00736 {
00737
00738
00739
00740 ctl_mmap image;
00741 ctl_mmap_open( &image, path );
00742 if( ctl_mmap_ready(&image) )
00743 {
00744 const BITMAPINFO* bmiIn;
00745 const void* bitsIn;
00746 BMI_ParseData( ctl_mmap_ptr(&image), bmiIn, bitsIn );
00747 *bmi = BMI_BMI_Copy(bmiIn);
00748 *bits = BMI_Unpack( bmiIn, bitsIn );
00749 return true;
00750 }
00751 return false;
00752 }
00753
00754 #else
00755 #endif
00756