#include "gblender.h" #include #include static void gblender_set_gamma_table( double gamma_value, unsigned short* gamma_ramp, unsigned char* gamma_ramp_inv ) { int gmax = (256 << GBLENDER_GAMMA_SHIFT)-1; if ( gamma_value <= 0 ) /* special case for sRGB */ { int ii; for ( ii = 0; ii < 256; ii++ ) { double x = (double)ii / 255.0; if ( x <= 0.03926 ) x = x/12.92; else x = pow( (x+0.055)/ 1.055, 2.4 ); gamma_ramp[ii] = (unsigned short)(gmax*x); } for ( ii = 0; ii < gmax; ii++ ) { double x = (double)ii / gmax; if ( x <= 0.00304 ) x = 12.92*x; else x = 1.055*pow(x,1/2.4) - 0.055; gamma_ramp_inv[ii] = (unsigned char)(255*x); } } else { int ii; double gamma_inv = 1.0f / gamma_value; /* voltage to linear */ for ( ii = 0; ii < 256; ii++ ) gamma_ramp[ii] = (unsigned short)( pow( (double)ii/255.0f, gamma_value )*gmax ); /* linear to voltage */ for ( ii = 0; ii < gmax; ii++ ) gamma_ramp_inv[ii] = (unsigned char)( pow( (double)ii/gmax, gamma_inv ) * 255.0f ); } } /* clear the cache */ static void gblender_clear( GBlender blender ) { int nn; GBlenderKey keys = blender->keys; if ( blender->channels ) { GBlenderChanKey chan_keys = (GBlenderChanKey) blender->keys; for ( nn = 0; nn < GBLENDER_KEY_COUNT; nn++ ) chan_keys[nn].index = -1; } else { for ( nn = 0; nn < GBLENDER_KEY_COUNT; nn++ ) keys[nn].cells = NULL; } } GBLENDER_APIDEF( void ) gblender_reset( GBlender blender ) { gblender_clear( blender ); blender->cache_r_back = -1; blender->cache_r_fore = -1; blender->cache_r_cells = 0; blender->cache_r_back = -1; blender->cache_r_fore = -1; blender->cache_r_cells = 0; blender->cache_back = 0; blender->cache_fore = 0xFFFFFF; blender->cache_cells = gblender_lookup( blender, blender->cache_back, blender->cache_fore ); #ifdef GBLENDER_STATS blender->stat_hits = 0; blender->stat_lookups = 0; blender->stat_keys = 0; blender->stat_clears = 0; #endif } GBLENDER_APIDEF( void ) gblender_init( GBlender blender, double gamma_value ) { blender->channels = 0; gblender_set_gamma_table ( gamma_value, blender->gamma_ramp, blender->gamma_ramp_inv ); gblender_reset( blender ); } /* recompute the grade levels of a given key */ static void gblender_reset_key( GBlender blender, GBlenderKey key ) { GBlenderPixel back = key->background; GBlenderPixel fore = key->foreground; GBlenderCell* gr = key->cells; int nn; int gmax = (256 << GBLENDER_GAMMA_SHIFT)-1; const unsigned char* gamma_ramp_inv = blender->gamma_ramp_inv; const unsigned short* gamma_ramp = blender->gamma_ramp; int r1,g1,b1,r2,g2,b2; r1 = ( back >> 16 ) & 255; g1 = ( back >> 8 ) & 255; b1 = ( back ) & 255; r2 = ( fore >> 16 ) & 255; g2 = ( fore >> 8 ) & 255; b2 = ( fore ) & 255; #ifdef GBLENDER_STORE_BYTES gr[0] = (unsigned char)r1; gr[1] = (unsigned char)g1; gr[2] = (unsigned char)b1; gr += 3; #else gr[0] = back; gr += 1; #endif r1 = gamma_ramp[r1]; g1 = gamma_ramp[g1]; b1 = gamma_ramp[b1]; r2 = gamma_ramp[r2]; g2 = gamma_ramp[g2]; b2 = gamma_ramp[b2]; for ( nn = 1; nn < GBLENDER_SHADE_COUNT; nn++ ) { int a = (nn << GBLENDER_SHADE_BITS); int r = ((r2-r1)*a + 128); int g = ((g2-g1)*a + 128); int b = ((g2-g1)*a + 128); r = (r + (r >> 8)) >> 8; g = (g + (g >> 8)) >> 8; b = (b + (b >> 8)) >> 8; r += r1; g += g1; b += b1; #if 0 r = ( r | -(r >> 8) ) & 255; g = ( g | -(g >> 8) ) & 255; b = ( b | -(b >> 8) ) & 255; #else if ( r < 0 ) r = 0; else if ( r > gmax ) r = gmax; if ( g < 0 ) g = 0; else if ( g > gmax ) g = gmax; if ( b < 0 ) b = 0; else if ( b > gmax ) b = gmax; #endif r = gamma_ramp_inv[r]; g = gamma_ramp_inv[g]; b = gamma_ramp_inv[b]; #ifdef GBLENDER_STORE_BYTES gr[0] = (unsigned char)r; gr[1] = (unsigned char)g; gr[2] = (unsigned char)b; gr += 3; #else gr[0] = (( r & 255 ) << 16) | (( g & 255 ) << 8 ) | (( b & 255 ) ) ; gr ++; #endif } } /* lookup the grades of a given (background,foreground) couple */ GBLENDER_APIDEF( GBlenderCell* ) gblender_lookup( GBlender blender, GBlenderPixel background, GBlenderPixel foreground ) { int idx, idx0; GBlenderKey key; #ifdef GBLENDER_STATS blender->stat_hits--; blender->stat_lookups++; #endif if ( blender->channels ) { /* set to normal mode */ blender->channels = 0; gblender_reset( blender ); } idx0 = ( background + foreground*63 ) % GBLENDER_KEY_COUNT; idx = idx0; do { key = blender->keys + idx; if ( key->cells == NULL ) goto NewNode; if ( key->background == background && key->foreground == foreground ) goto Exit; idx = (idx+1) % GBLENDER_KEY_COUNT; } while ( idx != idx0 ); /* the cache is full, clear it completely */ #ifdef GBLENDER_STATS blender->stat_clears++; gblender_clear( blender ); #endif NewNode: key->background = background; key->foreground = foreground; key->cells = blender->cells + \ idx0*(GBLENDER_SHADE_COUNT*GBLENDER_CELL_SIZE); gblender_reset_key( blender, key ); #ifdef GBLENDER_STATS blender->stat_keys++; #endif Exit: return key->cells; } static void gblender_reset_channel_key( GBlender blender, GBlenderChanKey key ) { int back = key->backfore & 255; int fore = (key->backfore >> 8) & 255; unsigned char* gr = (unsigned char*)blender->cells + key->index; int nn; const unsigned char* gamma_ramp_inv = blender->gamma_ramp_inv; const unsigned short* gamma_ramp = blender->gamma_ramp; int r1,r2; int gmax = (256 << GBLENDER_GAMMA_SHIFT)-1; r1 = back; r2 = fore; gr[0] = r1; gr++; r1 = gamma_ramp[r1]; r2 = gamma_ramp[r2]; for ( nn = 1; nn < GBLENDER_SHADE_COUNT; nn++ ) { int a = (nn << GBLENDER_SHADE_BITS); int r = ((r2-r1)*a + 128); r = (r + (r >> 8)) >> 8; r += r1; if ( r < 0 ) r = 0; else if ( r > gmax ) r = gmax; r = gamma_ramp_inv[r]; gr[0] = (unsigned char)r; gr++; } } GBLENDER_APIDEF( unsigned char* ) gblender_lookup_channel( GBlender blender, int background, int foreground ) { int idx, idx0; unsigned short backfore = (unsigned short)((foreground << 8) | background); GBlenderChanKey key; #ifdef GBLENDER_STATS blender->stat_hits--; blender->stat_lookups++; #endif if ( !blender->channels ) { /* set to normal mode */ blender->channels = 1; gblender_reset( blender ); } idx0 = ( background + foreground*63 ) % (2*GBLENDER_KEY_COUNT); idx = idx0; do { key = (GBlenderChanKey)blender->keys + idx; if ( key->index < 0 ) goto NewNode; if ( key->backfore == backfore ) goto Exit; idx = (idx+1) % (2*GBLENDER_KEY_COUNT); } while ( idx != idx0 ); /* the cache is full, clear it completely */ #ifdef GBLENDER_STATS blender->stat_clears++; gblender_clear( blender ); #endif NewNode: key->backfore = backfore; key->index = (signed short)( idx0 * GBLENDER_SHADE_COUNT ); gblender_reset_channel_key( blender, key ); #ifdef GBLENDER_STATS blender->stat_keys++; #endif Exit: return (unsigned char*)blender->cells + key->index; } #ifdef GBLENDER_STATS #include GBLENDER_APIDEF( void ) gblender_dump_stats( GBlender blender ) { printf( "hits = %ld, miss1 = %ld, miss2 = %ld, rate1=%.2f%%, rate2=%.2f%%\n", blender->stat_hits, blender->stat_lookups, blender->stat_keys, (100.0*blender->stat_hits) / (double)(blender->stat_hits + blender->stat_lookups), (100.0*blender->stat_lookups) / (double)( blender->stat_lookups + blender->stat_keys) ); } #endif