blob: f0346e1966e4c2b89070e17c9fe83730a0672678 [file] [log] [blame]
/****************************************************************************/
/* */
/* The FreeType project -- a free and portable quality TrueType renderer. */
/* */
/* Copyright 1996-1999 by */
/* D. Turner, R.Wilhelm, and W. Lemberg */
/* */
/* blitter.c: Support for blitting of bitmaps with various depth. */
/* */
/****************************************************************************/
#include "grblit.h"
#include "grobjs.h"
static
int compute_clips( grBlitter* blit,
int x_offset,
int y_offset )
{
int xmin, ymin, xmax, ymax, width, height, target_width;
/* perform clipping and setup variables */
width = blit->source.width;
height = blit->source.rows;
switch ( blit->source.mode )
{
case gr_pixel_mode_mono:
width = (width + 7) & -8;
break;
case gr_pixel_mode_pal4:
width = (width + 1) & -2;
break;
default:
;
}
xmin = x_offset;
ymin = y_offset;
xmax = xmin + width-1;
ymax = ymin + height-1;
/* clip if necessary */
if ( width == 0 || height == 0 ||
xmax < 0 || xmin >= blit->target.width ||
ymax < 0 || ymin >= blit->target.rows )
return 1;
/* set up clipping and cursors */
blit->yread = 0;
if ( ymin < 0 )
{
blit->yread -= ymin;
height += ymin;
blit->ywrite = 0;
}
else
blit->ywrite = ymin;
if ( ymax >= blit->target.rows )
height -= ymax - blit->target.rows + 1;
blit->xread = 0;
if ( xmin < 0 )
{
blit->xread -= xmin;
width += xmin;
blit->xwrite = 0;
}
else
blit->xwrite = xmin;
target_width = blit->target.width;
switch ( blit->target.mode )
{
case gr_pixel_mode_mono:
target_width = (target_width + 7) & -8;
break;
case gr_pixel_mode_pal4:
target_width = (target_width + 1) & -2;
break;
default:
;
}
blit->right_clip = xmax - target_width + 1;
if ( blit->right_clip > 0 )
width -= blit->right_clip;
else
blit->right_clip = 0;
blit->width = width;
blit->height = height;
/* set read and write to the top-left corner of the read */
/* and write areas before clipping. */
blit->read = (unsigned char*)blit->source.buffer;
blit->write = (unsigned char*)blit->target.buffer;
blit->read_line = blit->source.pitch;
blit->write_line = blit->target.pitch;
if ( blit->read_line < 0 )
blit->read -= (blit->source.rows-1) * blit->read_line;
if ( blit->write_line < 0 )
blit->write -= (blit->target.rows-1) * blit->write_line;
/* now go to the start line. Note that we do not move the */
/* x position yet, as this is dependent on the pixel format */
blit->read += blit->yread * blit->read_line;
blit->write += blit->ywrite * blit->write_line;
return 0;
}
/**************************************************************************/
/* */
/* <Function> blit_mono_to_mono */
/* */
/**************************************************************************/
static
void blit_mono_to_mono( grBlitter* blit,
grColor color )
{
int shift, left_clip, x, y;
byte* read;
byte* write;
(void)color; /* unused argument */
left_clip = ( blit->xread > 0 );
shift = ( blit->xwrite - blit->xread ) & 7;
read = blit->read + (blit->xread >> 3);
write = blit->write + (blit->xwrite >> 3);
if ( shift == 0 )
{
y = blit->height;
do
{
byte* _read = read;
byte* _write = write;
x = blit->width;
do
{
*_write++ |= *_read++;
x -= 8;
} while ( x > 0 );
read += blit->read_line;
write += blit->write_line;
y--;
} while ( y > 0 );
}
else
{
int first, last, count;
first = blit->xwrite >> 3;
last = (blit->xwrite + blit->width-1) >> 3;
count = last - first;
if ( blit->right_clip )
count++;
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
unsigned char old;
int shift2 = (8-shift);
if ( left_clip )
old = (*_read++) << shift2;
else
old = 0;
x = count;
while ( x > 0 )
{
unsigned char val;
val = *_read++;
*_write++ |= ( (val >> shift) | old );
old = val << shift2;
x--;
}
if ( !blit->right_clip )
*_write |= old;
read += blit->read_line;
write += blit->write_line;
y--;
} while ( y > 0 );
}
}
/**************************************************************************/
/* */
/* <Function> blit_mono_to_pal8 */
/* */
/**************************************************************************/
static
void blit_mono_to_pal8( grBlitter* blit,
grColor color )
{
int x, y;
unsigned int left_mask;
unsigned char* read;
unsigned char* write;
read = blit->read + (blit->xread >> 3);
write = blit->write + blit->xwrite;
left_mask = 0x80 >> (blit->xread & 7);
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
unsigned int mask = left_mask;
unsigned int val = *_read;
x = blit->width;
do
{
if ( mask == 0x80 )
val = *_read++;
if ( val & mask )
*_write = (unsigned char)color.value;
mask >>= 1;
if ( mask == 0 )
mask = 0x80;
_write++;
x--;
} while ( x > 0 );
read += blit->read_line;
write += blit->write_line;
y--;
} while ( y > 0 );
}
/**************************************************************************/
/* */
/* <Function> blit_mono_to_pal4 */
/* */
/**************************************************************************/
static
void blit_mono_to_pal4( grBlitter* blit,
grColor color )
{
int x, y, phase;
unsigned int left_mask;
unsigned char* read;
unsigned char* write;
unsigned char col;
col = color.value & 15;
read = blit->read + (blit->xread >> 3);
write = blit->write + (blit->xwrite >> 1);
/* now begin blit */
left_mask = 0x80 >> (blit->xread & 7);
phase = blit->xwrite & 1;
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
unsigned int mask = left_mask;
int _phase = phase;
unsigned int val = *_read;
x = blit->width;
do
{
if ( mask == 0x80 )
val = *_read++;
if ( val & mask )
{
if ( _phase )
*_write = (*_write & 0xF0) | col;
else
*_write = (*_write & 0x0F) | (col << 4);
}
mask >>= 1;
if ( mask == 0 )
mask = 0x80;
_write += _phase;
_phase ^= 1;
x--;
} while ( x > 0 );
read += blit->read_line;
write += blit->write_line;
y--;
} while ( y > 0 );
}
/**************************************************************************/
/* */
/* <Function> blit_mono_to_rgb16 */
/* */
/**************************************************************************/
static
void blit_mono_to_rgb16( grBlitter* blit,
grColor color )
{
int x, y;
unsigned int left_mask;
unsigned char* read;
unsigned char* write;
read = blit->read + (blit->xread >> 3);
write = blit->write + blit->xwrite*2;
left_mask = 0x80 >> (blit->xread & 7);
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
unsigned int mask = left_mask;
unsigned int val = *_read;
x = blit->width;
do
{
if ( mask == 0x80 )
val = *_read++;
if ( val & mask )
*(short*)_write = (short)color.value;
mask >>= 1;
if ( mask == 0 )
mask = 0x80;
_write +=2;
x--;
} while ( x > 0 );
read += blit->read_line;
write += blit->write_line;
y--;
} while ( y > 0 );
}
/**************************************************************************/
/* */
/* <Function> blit_mono_to_rgb24 */
/* */
/**************************************************************************/
static
void blit_mono_to_rgb24( grBlitter* blit,
grColor color )
{
int x, y;
unsigned int left_mask;
unsigned char* read;
unsigned char* write;
read = blit->read + (blit->xread >> 3);
write = blit->write + blit->xwrite*3;
left_mask = 0x80 >> (blit->xread & 7);
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
unsigned int mask = left_mask;
unsigned int val = *_read;
x = blit->width;
do
{
if ( mask == 0x80 )
val = *_read++;
if ( val & mask )
{
_write[0] = color.chroma[0];
_write[1] = color.chroma[1];
_write[2] = color.chroma[2];
}
mask >>= 1;
if ( mask == 0 )
mask = 0x80;
_write += 3;
x--;
} while ( x > 0 );
read += blit->read_line;
write += blit->write_line;
y--;
} while ( y > 0 );
}
/**************************************************************************/
/* */
/* <Function> blit_mono_to_rgb32 */
/* */
/**************************************************************************/
static
void blit_mono_to_rgb32( grBlitter* blit,
grColor color )
{
int x, y;
unsigned int left_mask;
unsigned char* read;
unsigned char* write;
read = blit->read + (blit->xread >> 3);
write = blit->write + blit->xwrite*4;
left_mask = 0x80 >> (blit->xread & 7);
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
unsigned int mask = left_mask;
unsigned int val = *_read;
x = blit->width;
do
{
if ( mask == 0x80 )
val = *_read++;
if ( val & mask )
{
/* this could be greatly optimised as a *(long*)_write = color.value */
/* but this wouldn't work on 64-bits systems... stupid C types! */
_write[0] = color.chroma[0];
_write[1] = color.chroma[1];
_write[2] = color.chroma[2];
_write[3] = color.chroma[3];
}
mask >>= 1;
if ( mask == 0 )
mask = 0x80;
_write += 4;
x--;
} while ( x > 0 );
read += blit->read_line;
write += blit->write_line;
y--;
} while ( y > 0 );
}
static
const grBlitterFunc gr_mono_blitters[gr_pixel_mode_max] =
{
0,
blit_mono_to_mono,
blit_mono_to_pal4,
blit_mono_to_pal8,
blit_mono_to_pal8,
blit_mono_to_rgb16,
blit_mono_to_rgb16,
blit_mono_to_rgb24,
blit_mono_to_rgb32
};
/*******************************************************************/
/* */
/* Saturation tables */
/* */
/*******************************************************************/
typedef struct grSaturation_
{
int count;
const byte* table;
} grSaturation;
static
const byte gr_saturation_5[8] = { 0, 1, 2, 3, 4, 4, 4, 4 };
static
const byte gr_saturation_17[32] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
};
static
grSaturation gr_saturations[ GR_MAX_SATURATIONS ] =
{
{ 5, gr_saturation_5 },
{ 17, gr_saturation_17 }
};
static
int gr_num_saturations = 2;
static
grSaturation* gr_last_saturation = gr_saturations;
extern
const byte* grGetSaturation( int num_grays )
{
/* first of all, scan the current saturations table */
grSaturation* sat = gr_saturations;
grSaturation* limit = sat + gr_num_saturations;
if ( num_grays < 2 )
{
grError = gr_err_bad_argument;
return 0;
}
for ( ; sat < limit; sat++ )
{
if ( sat->count == num_grays )
{
gr_last_saturation = sat;
return sat->table;
}
}
/* not found, simply create a new entry if there is room */
if (gr_num_saturations < GR_MAX_SATURATIONS)
{
int i;
const byte* table;
table = (const byte*)grAlloc( (3*num_grays-1)*sizeof(byte) );
if (!table) return 0;
sat->count = num_grays;
sat->table = table;
for ( i = 0; i < num_grays; i++, table++ )
*(unsigned char*)table = (unsigned char)i;
for ( i = 2*num_grays-1; i > 0; i--, table++ )
*(unsigned char*)table = (unsigned char)(num_grays-1);
gr_num_saturations++;
gr_last_saturation = sat;
return sat->table;
}
grError = gr_err_saturation_overflow;
return 0;
}
/*******************************************************************/
/* */
/* conversion tables */
/* */
/*******************************************************************/
typedef struct grConversion_
{
int target_grays;
int source_grays;
const byte* table;
} grConversion;
static
const byte gr_gray5_to_gray17[5] = { 0, 4, 8, 12, 16 };
static
const byte gr_gray5_to_gray128[5] = { 0, 32, 64, 96, 127 };
static
const unsigned char gr_gray17_to_gray128[17] =
{
0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 127
};
static
grConversion gr_conversions[ GR_MAX_CONVERSIONS ] =
{
{ 17, 5, gr_gray5_to_gray17 },
{ 128, 5, gr_gray5_to_gray128 },
{ 128, 17, gr_gray17_to_gray128 }
};
static
int gr_num_conversions = 3;
static
grConversion* gr_last_conversion = gr_conversions;
extern
const byte* grGetConversion( int target_grays,
int source_grays )
{
grConversion* conv = gr_conversions;
grConversion* limit = conv + gr_num_conversions;
if ( target_grays < 2 || source_grays < 2 )
{
grError = gr_err_bad_argument;
return 0;
}
/* otherwise, scan table */
for ( ; conv < limit; conv++ )
{
if ( conv->target_grays == target_grays &&
conv->source_grays == source_grays )
{
gr_last_conversion = conv;
return conv->table;
}
}
/* not found, add a new conversion to the table */
if (gr_num_conversions < GR_MAX_CONVERSIONS)
{
const byte* table;
int n;
table = (const byte*)grAlloc( source_grays*sizeof(byte) );
if (!table)
return 0;
conv->target_grays = target_grays;
conv->source_grays = source_grays;
conv->table = table;
for ( n = 0; n < source_grays; n++ )
((unsigned char*)table)[n] = (unsigned char)(n*(target_grays-1)) /
(source_grays-1);
gr_num_conversions++;
gr_last_conversion = conv;
return table;
}
grError = gr_err_conversion_overflow;
return 0;
}
/**************************************************************************/
/* */
/* <Function> blit_gray_to_gray */
/* */
/**************************************************************************/
static
void blit_gray_to_gray( grBlitter* blit,
const byte* saturation,
const byte* conversion )
{
int y;
unsigned char* read;
unsigned char* write;
unsigned char max1;
unsigned char max2;
max1 = (unsigned char)(blit->source.grays-1);
max2 = (unsigned char)(blit->target.grays-1);
read = (unsigned char*)blit->read + blit->xread;
write = (unsigned char*)blit->write + blit->xwrite;
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
int x = blit->width;
while (x > 0)
{
#ifdef GR_CONFIG_GRAY_SKIP_WHITE
unsigned char val = *_read;
if (val)
{
if (val == max)
*_write = max2;
else
*_write = saturation[ (int)*_write + conversion[ *_read ] ];
}
#else
*_write = saturation[ (int)*_write + conversion[ *_read ] ];
#endif
_write++;
_read++;
x--;
}
read += blit->read_line;
write += blit->write_line;
y--;
}
while (y > 0);
}
/**************************************************************************/
/* */
/* <Function> blit_gray_to_gray_simple */
/* */
/**************************************************************************/
static
void blit_gray_to_gray_simple( grBlitter* blit,
const byte* saturation )
{
int y;
unsigned char* read;
unsigned char* write;
unsigned char max;
max = (unsigned char)(blit->source.grays-1);
read = (unsigned char*)blit->read + blit->xread;
write = (unsigned char*)blit->write + blit->xwrite;
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
int x = blit->width;
while (x > 0)
{
#ifdef GR_CONFIG_GRAY_SKIP_WHITE
unsigned char val = *_read;
if (val)
{
if (val == max)
*_write = val;
else
*_write = saturation[ (int)*_write + *_read ];
}
#else
*_write = saturation[ (int)*_write + *_read ];
#endif
_write++;
_read++;
x--;
}
read += blit->read_line;
write += blit->write_line;
y--;
}
while (y > 0);
}
#define compose_pixel( a, b, n, max ) \
{ \
int d, half = max >> 1; \
\
d = (int)b.chroma[0] - a.chroma[0]; \
a.chroma[0] += (n*d + half)/max; \
\
d = (int)b.chroma[1] - a.chroma[1]; \
a.chroma[1] += (n*d + half)/max; \
\
d = (int)b.chroma[2] - a.chroma[2]; \
a.chroma[2] += (n*d + half)/max; \
}
#define extract555( pixel, color ) \
color.chroma[0] = (pixel >> 10) & 0x1F; \
color.chroma[1] = (pixel >> 5) & 0x1F; \
color.chroma[2] = (pixel ) & 0x1F;
#define extract565( pixel, color ) \
color.chroma[0] = (pixel >> 11) & 0x1F; \
color.chroma[1] = (pixel >> 5) & 0x3F; \
color.chroma[2] = (pixel ) & 0x1F;
#define inject555( color ) \
( ( (unsigned short)color.chroma[0] << 10 ) | \
( (unsigned short)color.chroma[1] << 5 ) | \
color.chroma[2] )
#define inject565( color ) \
( ( (unsigned short)color.chroma[0] << 11 ) | \
( (unsigned short)color.chroma[1] << 5 ) | \
color.chroma[2] )
/**************************************************************************/
/* */
/* <Function> blit_gray_to_555 */
/* */
/**************************************************************************/
static
void blit_gray_to_555( grBlitter* blit,
grColor color,
int max )
{
int y;
unsigned char* read;
unsigned char* write;
long color2;
read = blit->read + blit->xread;
write = blit->write + 2*blit->xwrite;
/* convert color to R:G:B triplet */
color2 = color.value;
extract555( color2, color );
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
int x = blit->width;
while (x > 0)
{
unsigned char val;
val = *_read;
if (val)
{
unsigned short* pixel = (unsigned short*)_write;
if (val == max)
{
pixel[0] = (short)color2;
}
else
{
/* compose gray value */
unsigned short pix16 = *pixel;
grColor pix;
extract555( pix16, pix );
compose_pixel( pix, color, val, max );
*pixel = inject555(pix);
}
}
_write += 2;
_read ++;
x--;
}
read += blit->read_line;
write += blit->write_line;
y--;
}
while (y > 0);
}
/**************************************************************************/
/* */
/* <Function> blit_gray_to_565 */
/* */
/**************************************************************************/
static
void blit_gray_to_565( grBlitter* blit,
grColor color,
int max )
{
int y;
unsigned char* read;
unsigned char* write;
long color2;
read = blit->read + blit->xread;
write = blit->write + 2*blit->xwrite;
color2 = color.value;
extract565( color2, color );
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
int x = blit->width;
while (x > 0)
{
unsigned char val;
val = *_read;
if (val)
{
unsigned short* pixel = (unsigned short*)_write;
if (val == max)
{
pixel[0] = (short)color2;
}
else
{
/* compose gray value */
unsigned short pix16 = *pixel;
grColor pix;
extract565( pix16, pix );
compose_pixel( pix, color, val, max );
*pixel = inject565( pix );
}
}
_write +=2;
_read ++;
x--;
}
read += blit->read_line;
write += blit->write_line;
y--;
}
while (y > 0);
}
/**************************************************************************/
/* */
/* <Function> blit_gray_to_24 */
/* */
/**************************************************************************/
static
void blit_gray_to_24( grBlitter* blit,
grColor color,
int max )
{
int y;
unsigned char* read;
unsigned char* write;
read = blit->read + blit->xread;
write = blit->write + 3*blit->xwrite;
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
int x = blit->width;
while (x > 0)
{
unsigned char val;
val = *_read;
if (val)
{
if (val == max)
{
_write[0] = color.chroma[0];
_write[1] = color.chroma[1];
_write[2] = color.chroma[2];
}
else
{
/* compose gray value */
grColor pix;
pix.chroma[0] = _write[0];
pix.chroma[1] = _write[1];
pix.chroma[2] = _write[2];
compose_pixel( pix, color, val, max );
_write[0] = pix.chroma[0];
_write[1] = pix.chroma[1];
_write[2] = pix.chroma[2];
}
}
_write += 3;
_read ++;
x--;
}
read += blit->read_line;
write += blit->write_line;
y--;
}
while (y > 0);
}
/**************************************************************************/
/* */
/* <Function> blit_gray_to_32 */
/* */
/**************************************************************************/
static
void blit_gray_to_32( grBlitter* blit,
grColor color,
int max )
{
int y;
unsigned char* read;
unsigned char* write;
read = blit->read + blit->xread;
write = blit->write + 4*blit->xwrite;
y = blit->height;
do
{
unsigned char* _read = read;
unsigned char* _write = write;
int x = blit->width;
while (x > 0)
{
unsigned char val;
val = *_read;
if (val)
{
if (val == max)
{
_write[0] = color.chroma[0];
_write[1] = color.chroma[1];
_write[2] = color.chroma[2];
_write[3] = color.chroma[3];
}
else
{
/* compose gray value */
grColor pix;
pix.chroma[0] = _write[0];
pix.chroma[1] = _write[1];
pix.chroma[2] = _write[2];
compose_pixel( pix, color, val, max );
_write[0] = pix.chroma[0];
_write[1] = pix.chroma[1];
_write[2] = pix.chroma[2];
}
}
_write += 4;
_read ++;
x--;
}
read += blit->read_line;
write += blit->write_line;
y--;
}
while (y > 0);
}
/**********************************************************************
*
* <Function>
* grBlitGlyphBitmap
*
* <Description>
* writes a given glyph bitmap to a target surface.
*
* <Input>
* surface :: handle to target surface
* x :: position of left-most pixel of glyph image in surface
* y :: position of top-most pixel of glyph image in surface
* bitmap :: source glyph image
*
* <Return>
* Error code. 0 means success
*
**********************************************************************/
typedef void (*grColorGlyphBlitter)( grBlitter* blit,
grColor color,
int max_gray );
static
const grColorGlyphBlitter gr_color_blitters[gr_pixel_mode_max] =
{
0,
0,
0,
0,
blit_gray_to_555,
blit_gray_to_565,
blit_gray_to_24,
blit_gray_to_32
};
extern int grBlitGlyphToBitmap( grBitmap* target,
grBitmap* glyph,
grPos x,
grPos y,
grColor color )
{
grBlitter blit;
grPixelMode mode;
/* check arguments */
if (!target || !glyph)
{
grError = gr_err_bad_argument;
return -1;
}
/* set up blitter and compute clipping. Return immediately if needed */
blit.source = *glyph;
blit.target = *target;
mode = target->mode;
if ( compute_clips( &blit, x, y ) )
return 0;
/* handle monochrome bitmap blitting */
if (glyph->mode == gr_pixel_mode_mono)
{
if ( mode <= gr_pixel_mode_none || mode >= gr_pixel_mode_max )
{
grError = gr_err_bad_source_depth;
return -1;
}
gr_mono_blitters[mode]( &blit, color );
goto End;
}
/* handle gray bitmap composition */
if (glyph->mode == gr_pixel_mode_gray &&
glyph->grays > 1 )
{
int target_grays = target->grays;
int source_grays = glyph->grays;
const byte* saturation;
if ( mode == gr_pixel_mode_gray && target_grays > 1 )
{
/* rendering into a gray target - use special composition */
/* routines.. */
if ( gr_last_saturation->count == target_grays )
saturation = gr_last_saturation->table;
else
{
saturation = grGetSaturation( target_grays );
if (!saturation) return -3;
}
if ( target_grays == source_grays )
blit_gray_to_gray_simple( &blit, saturation );
else
{
const byte* conversion;
if ( gr_last_conversion->target_grays == target_grays &&
gr_last_conversion->source_grays == source_grays )
conversion = gr_last_conversion->table;
else
{
conversion = grGetConversion( target_grays, source_grays );
if (!conversion) return -3;
};
blit_gray_to_gray( &blit, saturation, conversion );
}
}
else
{
/* rendering into a color target */
if ( mode <= gr_pixel_mode_gray ||
mode >= gr_pixel_mode_max )
{
grError = gr_err_bad_target_depth;
return -1;
}
gr_color_blitters[mode]( &blit, color, source_grays-1 );
}
goto End;
}
/* we don't support the blitting of bitmaps of the following */
/* types : pal4, pal8, rgb555, rgb565, rgb24, rgb32 */
/* */
grError = gr_err_bad_source_depth;
return -2;
End:
return 0;
}
/* End */