#include "ftxsbit.h"
#include "ttobjs.h"
#include "ttfile.h"
#include "ttload.h"
#include "ttmemory.h"
#include "tttags.h"
#include "ttextend.h"
#include "ttdebug.h"
#define SBIT_ID Build_Extension_ID( 's', 'b', 'i', 't' )
#undef TT_COMPONENT
#define TT_COMPONENT trace_bitmap
#define DEFINE_LOCALS DEFINE_LOAD_LOCALS( face->stream )
#define DEFINE_LOCALS_WO_FRAME DEFINE_LOAD_LOCALS_WO_FRAME( face->stream )
static
TT_Error Load_BitmapData( TT_SBit_Image* image,
Int image_size,
Byte x_offset,
Byte y_offset,
UShort source_width,
UShort source_height,
Bool byte_padded )
{
DEFINE_LOCALS;
Int count;
Int loaded;
UShort buff;
PByte line;
PByte limit;
if ( ( y_offset + source_height > image->map.rows ) ||
( x_offset + source_width > image->map.width ) )
return TT_Err_Invalid_Argument;
if ( ACCESS_Frame( image_size ) )
return error;
buff = 0;
loaded = 0;
line = (PByte)image->map.bitmap +
y_offset * image->map.cols;
limit = (PByte)image->map.bitmap +
( y_offset + source_height ) * image->map.cols;
for ( ; line < limit; line += image->map.cols )
{
PByte ptr;
ptr = line + x_offset / 8;
count = source_width;
buff >>= x_offset % 8;
loaded += x_offset % 8;
while ( count >= 8 )
{
if ( loaded < 8 )
{
buff |= ((UShort)GET_Byte()) << (8 - loaded);
loaded += 8;
}
*ptr++ |= (Byte)(buff >> 8);
buff <<= 8;
loaded -= 8;
count -= 8;
}
if ( count > 0 )
{
if ( loaded < count )
{
buff |= ((UShort)GET_Byte()) << (8 - loaded);
loaded += 8;
}
*ptr |= ((Byte)(buff >> 8)) & ~(0xFF >> count);
buff <<= count;
loaded -= count;
}
if ( byte_padded )
{
buff = 0;
loaded = 0;
}
}
FORGET_Frame();
return TT_Err_Ok;
}
static
void Crop_Bitmap( TT_SBit_Image* image )
{
Int rows, count;
Long line_len;
PByte line;
{
line = (PByte)image->map.bitmap;
rows = image->map.rows;
line_len = image->map.cols;
for ( count = 0; count < rows; count++ )
{
PByte cur = line;
PByte limit = line + line_len;
for ( ; cur < limit; cur++ )
if ( cur[0] )
goto Found_Top;
line = limit;
}
Found_Top:
if ( count >= rows )
goto Empty_Bitmap;
if ( count > 0 )
{
line = (PByte)image->map.bitmap;
MEM_Move( line, line + count*line_len, (rows-count) * line_len );
image->metrics.bbox.yMax -= count;
image->metrics.vertBearingY -= count;
image->metrics.horiBearingY -= count;
image->map.rows -= count;
rows -= count;
}
}
{
line = (PByte)image->map.bitmap + (rows-1) * line_len;
for ( count = 0; count < rows; count++ )
{
PByte cur = line;
PByte limit = line + line_len;
for ( ; cur < limit; cur++ )
if ( cur[0] )
goto Found_Bottom;
line -= line_len;
}
Found_Bottom:
if ( count > 0 )
{
image->metrics.bbox.yMin += count;
image->map.rows -= count;
rows -= count;
}
}
do
{
PByte limit;
line = (PByte)image->map.bitmap;
limit = line + rows * line_len;
for ( ; line < limit; line += line_len )
if ( line[0] & 0x80 )
goto Found_Left;
line = (PByte)image->map.bitmap;
limit = line + rows * line_len;
for ( ; line < limit; line += line_len )
{
Int n, width = image->map.width;
Byte old;
PByte cur = line;
old = cur[0] << 1;
for ( n = 8; n < width; n += 8 )
{
Byte val;
val = cur[1];
cur[0] = old | (val >> 7);
old = val << 1;
cur++;
}
cur[0] = old;
}
image->map.width--;
image->metrics.horiBearingX++;
image->metrics.vertBearingX++;
image->metrics.bbox.xMin++;
} while ( image->map.width > 0 );
Found_Left:
do
{
Int right = image->map.width-1;
PByte limit;
Byte mask;
line = (PByte)image->map.bitmap + (right >> 3);
limit = line + rows*line_len;
mask = 0x80 >> (right & 7);
for ( ; line < limit; line += line_len )
if ( line[0] & mask )
goto Found_Right;
image->map.width--;
image->metrics.bbox.xMax--;
} while ( image->map.width > 0 );
Found_Right:
return;
Empty_Bitmap:
image->map.width = 0;
image->map.rows = 0;
image->map.cols = 0;
image->map.size = 0;
}
static
TT_Error Load_Range_Codes( TT_SBit_Range* range,
PFace face,
Bool load_offsets )
{
DEFINE_LOCALS;
ULong count, n, size;
(void)face;
if ( ACCESS_Frame( 4L ) )
goto Exit;
count = GET_ULong();
FORGET_Frame();
range->num_glyphs = count;
if ( load_offsets )
{
if ( ALLOC_ARRAY( range->glyph_offsets, count, ULong ) )
goto Exit;
size = count * 4L;
}
else
size = count * 2L;
if ( ALLOC_ARRAY ( range->glyph_codes, count, UShort ) ||
ACCESS_Frame( size ) )
goto Exit;
for ( n = 0; n < count; n++ )
{
range->glyph_codes[n] = GET_UShort();
if ( load_offsets )
range->glyph_offsets[n] = (ULong)range->image_offset + GET_UShort();
}
FORGET_Frame();
Exit:
return error;
}
static
TT_Error Load_SBit_Range( TT_SBit_Strike* strike,
TT_SBit_Range* range,
PFace face )
{
DEFINE_LOCALS;
UShort format;
(void)face;
(void)strike;
format = range->index_format;
PTRACE6(( "Index Format: %d\n", format ));
switch( format )
{
case 1:
case 3:
{
UShort num_glyphs, size_elem;
Bool large = (format == 1);
ULong* cur;
num_glyphs = range->last_glyph - range->first_glyph + 1;
PTRACE5(( " num glyphs: %hu\n", num_glyphs ));
range->num_glyphs = num_glyphs;
num_glyphs++;
size_elem = large ? 4 : 2;
if ( ALLOC_ARRAY( range->glyph_offsets, num_glyphs, ULong ) ||
ACCESS_Frame( num_glyphs * size_elem ) )
return error;
cur = range->glyph_offsets;
while ( num_glyphs > 0 )
{
cur[0] = (TT_ULong)( range->image_offset +
(large ? GET_ULong() : GET_UShort()) );
PTRACE7(( " offset: %d\n", cur[0] ));
cur++;
num_glyphs--;
}
FORGET_Frame();
}
break;
case 2:
case 4:
case 5:
{
error = 0;
if ( format != 4 )
{
TT_SBit_Metrics* metrics;
if ( ACCESS_Frame( 12L ) )
return error;
range->image_size = GET_ULong();
metrics = &range->metrics;
metrics->height = GET_Byte();
metrics->width = GET_Byte();
metrics->horiBearingX = GET_Char();
metrics->horiBearingY = GET_Char();
metrics->horiAdvance = GET_Byte();
metrics->vertBearingX = GET_Char();
metrics->vertBearingY = GET_Char();
metrics->vertAdvance = GET_Byte();
FORGET_Frame();
}
if ( format != 2 )
error = Load_Range_Codes( range, face, (format == 4) );
}
break;
default:
error = TT_Err_Invalid_File_Format;
}
PTRACE3(( "Embedded Bitmap Location Tables loaded.\n" ));
return error;
}
static
TT_Error Load_TrueType_Eblc( PFace face,
TT_EBLC* eblc )
{
DEFINE_LOCALS;
ULong eblc_offset;
UShort i;
Long table;
TT_SBit_Strike* strike;
PTRACE2(( "Load_EBLC_Table( %08lx )\n", (long)face ));
eblc->version = 0;
table = TT_LookUp_Table( face, TTAG_EBLC );
if ( table < 0 )
table = TT_LookUp_Table( face, TTAG_bloc );
if ( table < 0 )
return TT_Err_Ok;
eblc_offset = face->dirTables[table].Offset;
if ( FILE_Seek( eblc_offset ) ||
ACCESS_Frame( 8L ) )
return error;
eblc->version = GET_ULong();
eblc->num_strikes = GET_ULong();
FORGET_Frame();
PTRACE2(( "-- Tables count: %12u\n", eblc->num_strikes ));
PTRACE2(( "-- Format version: %08lx\n", eblc->version ));
if ( eblc->version != 0x00020000 )
{
PERROR(( "Invalid file format!\n" ));
return TT_Err_Invalid_File_Format;
}
if ( ALLOC_ARRAY( eblc->strikes, eblc->num_strikes, TT_SBit_Strike ) ||
ACCESS_Frame( 48L * eblc->num_strikes ) )
return error;
strike = eblc->strikes;
for ( i = 0; i < eblc->num_strikes; i++, strike++ )
{
TT_SBit_Line_Metrics* metrics;
Int count;
strike->ranges_offset = GET_ULong();
(void) GET_ULong();
strike->num_ranges = GET_ULong();
strike->color_ref = GET_ULong();
metrics = &strike->hori;
for ( count = 2; count > 0; count-- )
{
metrics->ascender = GET_Char();
metrics->descender = GET_Char();
metrics->max_width = GET_Byte();
metrics->caret_slope_numerator = GET_Char();
metrics->caret_slope_denominator = GET_Char();
metrics->caret_offset = GET_Char();
metrics->min_origin_SB = GET_Char();
metrics->min_advance_SB = GET_Char();
metrics->max_before_BL = GET_Char();
metrics->min_after_BL = GET_Char();
metrics->pads[0] = GET_Char();
metrics->pads[1] = GET_Char();
metrics = &strike->vert;
}
strike->start_glyph = GET_UShort();
strike->end_glyph = GET_UShort();
strike->x_ppem = GET_Byte();
strike->y_ppem = GET_Byte();
strike->bit_depth = GET_Byte();
strike->flags = GET_Char();
PTRACE4(( " start - end - ppemX - ppemY\n" ));
PTRACE4(( " %04d - %04d - %3u - %3u\n",
strike->start_glyph,
strike->end_glyph,
strike->x_ppem,
strike->y_ppem ));
}
FORGET_Frame();
strike = eblc->strikes;
for ( i = 0; i < eblc->num_strikes; i++, strike++ )
{
TT_SBit_Range* range;
UShort count = strike->num_ranges;
if ( ALLOC_ARRAY( strike->sbit_ranges,
strike->num_ranges,
TT_SBit_Range ) ||
FILE_Seek( eblc_offset + strike->ranges_offset ) ||
ACCESS_Frame( strike->num_ranges * 8L ) )
return error;
for ( range = strike->sbit_ranges; count > 0; count--, range++ )
{
range->first_glyph = GET_UShort();
range->last_glyph = GET_UShort();
range->table_offset = eblc_offset + strike->ranges_offset +
GET_ULong();
}
FORGET_Frame();
range = strike->sbit_ranges;
for ( count = strike->num_ranges; count > 0; count--, range++ )
{
if ( FILE_Seek( range->table_offset ) ||
ACCESS_Frame( 8L ) )
return error;;
range->index_format = GET_UShort();
range->image_format = GET_UShort();
range->image_offset = GET_ULong();
FORGET_Frame();
error = Load_SBit_Range( strike, range, face );
if (error) return error;
}
}
return TT_Err_Ok;
}
static
void Free_TrueType_Eblc( TT_EBLC* eblc )
{
if ( eblc )
{
ULong i;
TT_SBit_Strike* strike = eblc->strikes;
strike = eblc->strikes;
for ( i = eblc->num_strikes; i > 0; i--, strike++ )
{
TT_SBit_Range* range = strike->sbit_ranges;
Int n;
for ( n = strike->num_ranges; n > 0; n--, range++ )
{
FREE( range->glyph_offsets );
FREE( range->glyph_codes );
}
FREE( strike->sbit_ranges );
strike->num_ranges = 0;
}
FREE( eblc->strikes );
eblc->num_strikes = 0;
eblc->version = 0;
}
}
static
TT_Error Load_SBit_Metrics( TT_Big_Glyph_Metrics* metrics,
TT_SBit_Range* range,
ULong ebdt_offset )
{
TT_Error error;
Byte height, width;
if ( ( ( range->index_format == 2 ) || ( range->index_format == 5 ) ) &&
( range->image_format == 5 ) )
{
TT_SBit_Metrics* rmetrics = &range->metrics;
metrics->bbox.xMin = rmetrics->horiBearingX;
metrics->bbox.xMax = metrics->bbox.xMin + rmetrics->width;
metrics->bbox.yMax = rmetrics->horiBearingY;
metrics->bbox.yMin = metrics->bbox.yMax - rmetrics->height;
metrics->horiBearingX = rmetrics->horiBearingX;
metrics->horiBearingY = metrics->bbox.yMax;
metrics->horiAdvance = rmetrics->horiAdvance;
metrics->vertBearingX = rmetrics->vertBearingX;
metrics->vertBearingY = rmetrics->vertBearingY;
metrics->vertAdvance = rmetrics->vertAdvance;
return TT_Err_Ok;
}
switch ( range->image_format )
{
case 1:
case 2:
case 6:
case 7:
case 8:
case 9:
{
Long length = 5L;
if ( range->image_format == 8 )
length++;
if ( ACCESS_Frame( length ) )
return error;
height = GET_Byte();
width = GET_Byte();
metrics->horiBearingX = GET_Char();
metrics->horiBearingY = GET_Char();
metrics->horiAdvance = GET_Byte();
FORGET_Frame();
metrics->bbox.xMin = metrics->horiBearingX;
metrics->bbox.yMax = metrics->horiBearingY;
metrics->bbox.xMax = metrics->bbox.xMin + width;
metrics->bbox.yMin = metrics->bbox.yMax - height;
if ( ( range->image_format >= 6 ) && ( range->image_format != 8 ) )
{
if ( ACCESS_Frame( 3L ) )
return error;
metrics->vertBearingX = (Int)GET_Char();
metrics->vertBearingY = (Int)GET_Char();
metrics->vertAdvance = (Int)GET_Char();
FORGET_Frame();
}
else
{
metrics->vertBearingX = 0;
metrics->vertBearingY = 0;
metrics->vertAdvance = 0;
}
}
break;
case 5:
break;
default:
PERROR(( "Unsupported embedded bitmap format!\n" ));
return TT_Err_Invalid_File_Format;
}
return TT_Err_Ok;
}
static
TT_Error Load_SBit_Image( TT_SBit_Strike strike,
UShort glyph_index,
Byte x_offset,
Byte y_offset,
ULong ebdt_offset,
TT_SBit_Image* image,
UShort component_depth )
{
TT_Error error;
Byte height, width;
ULong bitmap_offset;
TT_SBit_Range* range = 0;
TT_Big_Glyph_Metrics metrics;
{
UShort count = strike.num_ranges;
TT_SBit_Range* cur = strike.sbit_ranges;
for ( ; count > 0; count--, cur++ )
{
switch ( cur->index_format )
{
case 1:
case 2:
case 3:
if ( glyph_index >= cur->first_glyph &&
glyph_index <= cur->last_glyph )
{
UShort delta = glyph_index - cur->first_glyph;
range = cur;
bitmap_offset = cur->index_format == 2
? cur->image_offset + cur->image_size * delta
: cur->glyph_offsets[delta];
goto Found;
}
break;
case 4:
case 5:
{
UShort n;
for ( n = 0; n < cur->num_glyphs; n++ )
if ( cur->glyph_codes[n] == glyph_index )
{
range = cur;
bitmap_offset = cur->index_format == 4
? cur->glyph_offsets[n]
: cur->image_offset + cur->image_size * n;
goto Found;
}
}
break;
default:
return TT_Err_Invalid_Glyph_Index;
}
}
return TT_Err_Invalid_Glyph_Index;
}
Found:
if ( FILE_Seek( ebdt_offset + bitmap_offset ) )
return error;
error = Load_SBit_Metrics( &metrics, range, ebdt_offset );
if ( error )
return error;
width = metrics.bbox.xMax - metrics.bbox.xMin;
height = metrics.bbox.yMax - metrics.bbox.yMin;
if ( !component_depth )
{
image->metrics = metrics;
image->map.width = width;
image->map.rows = height;
image->map.cols = (width + 7) >> 3;
image->map.size = height * image->map.cols;
if ( REALLOC( image->map.bitmap, image->map.size ) )
return error;
MEM_Set( image->map.bitmap, 0, image->map.size );
}
switch ( range->image_format )
{
case 1:
case 6:
error = Load_BitmapData( image,
height * (( width + 7 ) >> 3),
x_offset, y_offset,
width, height,
1 );
if ( error )
return error;
break;
case 2:
case 5:
case 7:
error = Load_BitmapData( image,
(width * height + 7) >> 3,
x_offset, y_offset,
width, height, 0 );
if ( error )
return error;
break;
case 8:
case 9:
{
TT_SBit_Component* component_array;
UShort num_components;
Int i;
if ( ACCESS_Frame( 2L ) )
return error;
num_components = GET_UShort();
FORGET_Frame();
MEM_Alloc( component_array,
sizeof ( TT_SBit_Component ) * num_components );
if ( ACCESS_Frame( 4L * num_components ) )
return error;
for ( i = 0; i < num_components; i++ )
{
component_array[i].glyph_code = GET_UShort();
component_array[i].x_offset = GET_Char();
component_array[i].y_offset = GET_Char();
}
FORGET_Frame();
component_depth++;
for ( i = 0; i < num_components; i++ )
{
error = Load_SBit_Image( strike, component_array[i].glyph_code,
component_array[i].x_offset,
component_array[i].y_offset,
ebdt_offset,
image,
component_depth );
if ( error )
return error;
}
FREE( component_array );
break;
default:
return TT_Err_Invalid_File_Format;
}
}
return TT_Err_Ok;
}
static
TT_Error Load_TrueType_Ebdt( PFace face,
TT_SBit_Strike strike,
ULong glyph_index,
TT_SBit_Image* image )
{
DEFINE_LOCALS;
ULong ebdt_offset;
ULong version;
Long i;
i = TT_LookUp_Table( face, TTAG_EBDT );
if ( i < 0 )
i = TT_LookUp_Table( face, TTAG_bdat );
if ( i < 0 )
return TT_Err_Table_Missing;
ebdt_offset = face->dirTables[i].Offset;
if ( FILE_Seek( ebdt_offset ) ||
ACCESS_Frame( 4L ) )
return error;
version = GET_ULong();
FORGET_Frame();
PTRACE2(( "-- Format version : %08lx\n", version ));
if ( version != 0x00020000 )
{
PERROR(( "Invalid file format!\n" ));
return TT_Err_Invalid_File_Format;
}
error = Load_SBit_Image( strike,
glyph_index,
0, 0,
ebdt_offset,
image,
0 );
if ( error )
return error;
return TT_Err_Ok;
}
static TT_Error EBLC_Create( void* ext,
PFace face )
{
TT_EBLC* eblc = (TT_EBLC*)ext;
if ( !eblc )
return TT_Err_Ok;
return Load_TrueType_Eblc( face, eblc );
}
static TT_Error EBLC_Destroy( void* ext,
PFace face )
{
TT_EBLC* eblc = (TT_EBLC*)ext;
(void)face;
if ( eblc )
Free_TrueType_Eblc( eblc );
return TT_Err_Ok;
}
EXPORT_FUNC
TT_Error TT_Init_SBit_Extension( TT_Engine engine )
{
PEngine_Instance _engine = HANDLE_Engine( engine );
TT_Error error;
if ( !_engine )
return TT_Err_Invalid_Engine;
error = TT_Register_Extension( _engine,
SBIT_ID,
sizeof ( TT_EBLC ),
EBLC_Create,
EBLC_Destroy );
return error;
}
EXPORT_FUNC
TT_Error TT_Get_Face_Bitmaps( TT_Face face,
TT_EBLC* eblc_table )
{
PFace faze = HANDLE_Face( face );
TT_EBLC* eblc;
TT_Error error;
error = TT_Extension_Get( faze, SBIT_ID, (void**)&eblc );
if ( !error )
{
if ( eblc->version )
{
*eblc_table = *eblc;
return TT_Err_Ok;
}
error = TT_Err_Table_Missing;
}
eblc_table->version = 0;
eblc_table->num_strikes = 0;
eblc_table->strikes = 0;
return error;
}
EXPORT_FUNC
TT_Error TT_Get_SBit_Strike( TT_Face face,
TT_Instance instance,
TT_SBit_Strike* strike )
{
TT_Error error;
PFace faze = HANDLE_Face( face );
PInstance ins = HANDLE_Instance( instance );
TT_EBLC* eblc;
TT_Int x_ppem, y_ppem;
if ( !strike || !ins || ins->owner != faze )
return TT_Err_Invalid_Argument;
error = TT_Extension_Get( faze, SBIT_ID, (void**)&eblc );
if ( error )
goto Exit;
{
UShort count = eblc->num_strikes;
TT_SBit_Strike* cur = eblc->strikes;
x_ppem = ins->metrics.x_ppem;
y_ppem = ins->metrics.y_ppem;
MEM_Set( strike, 0, sizeof ( TT_SBit_Strike ) );
for ( ; count > 0; count--, cur++ )
if ( cur->x_ppem == x_ppem &&
cur->y_ppem == y_ppem )
{
*strike = *cur;
break;
}
if ( !strike->num_ranges )
error = TT_Err_Invalid_PPem;
}
Exit:
return error;
}
EXPORT_FUNC
TT_Error TT_Load_Glyph_Bitmap( TT_Face face,
TT_Instance instance,
TT_UShort glyph_index,
TT_SBit_Image* image )
{
TT_Stream stream;
TT_Error error;
PFace faze = HANDLE_Face( face );
PInstance ins = HANDLE_Instance( instance );
TT_SBit_Strike strike;
if ( ins->owner != faze )
{
error = TT_Err_Invalid_Argument;
goto Fail;
}
error = TT_Get_SBit_Strike( face, instance, &strike );
if ( error )
goto Fail;
if ( glyph_index < strike.start_glyph ||
glyph_index > strike.end_glyph )
{
error = TT_Err_Invalid_Glyph_Index;
goto Fail;
}
{
image->bit_depth = 1;
if ( !USE_Stream( faze->stream, stream ) )
{
error = Load_TrueType_Ebdt( faze, strike, glyph_index, image );
DONE_Stream( stream );
if ( !error )
{
image->map.flow = TT_Flow_Down;
Crop_Bitmap( image );
{
TT_Big_Glyph_Metrics* metrics = &image->metrics;
metrics->bbox.xMin *= 64;
metrics->bbox.xMax *= 64;
metrics->bbox.yMax *= 64;
metrics->bbox.yMin *= 64;
metrics->horiBearingX *= 64;
metrics->horiBearingY *= 64;
metrics->horiAdvance *= 64;
metrics->vertBearingX *= 64;
metrics->vertBearingY *= 64;
metrics->vertAdvance *= 64;
}
goto Exit;
}
}
}
Fail:
image->map.width = 0;
image->map.rows = 0;
image->map.cols = 0;
image->map.size = 0;
image->map.bitmap = 0;
image->map.flow = 0;
image->bit_depth = 0;
Exit:
return error;
}
EXPORT_FUNC
TT_Error TT_New_SBit_Image( TT_SBit_Image** image )
{
return MEM_Alloc( *image, sizeof ( **image ) );
}
EXPORT_FUNC
void TT_Done_SBit_Image( TT_SBit_Image* image )
{
FREE( image->map.bitmap );
FREE( image );
}