You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libksquirrel/kernel/kls_ttf/ftview/gblender.cpp

382 lines
8.3 KiB

#include "gblender.h"
#include <stdlib.h>
#include <math.h>
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 <stdio.h>
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