blob: 865b7d8be9d8e36ea5649c9962e76dc9fdcfe204 [file] [log] [blame]
/***************************************************************************/
/* */
/* gxlookuptbl.c */
/* */
/* AAT/TrueTypeGX lookup table related types and functions */
/* (body) */
/* */
/* Copyright 2003 by */
/* Masatake YAMATO and Redhat K.K. */
/* */
/* This file may only be used, */
/* modified, and distributed under the terms of the FreeType project */
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
/* this file you indicate that you have read the license and */
/* understand and accept it fully. */
/* */
/***************************************************************************/
/***************************************************************************/
/* Development of the code in this file is support of */
/* Information-technology Promotion Agency, Japan. */
/***************************************************************************/
#include <ft2build.h>
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_DEBUG_H
#include "gxlookuptbl.h"
#include "gxerrors.h"
typedef FT_Error (*gx_LookupTable_loader) ( GX_LookupTable lookup_table,
FT_Stream stream );
typedef void (*gx_LookupTable_finalizer) ( FT_Memory memory,
GX_LookupTable lookup_table );
typedef FT_Error (*gx_LookupTable_traverser) ( GX_LookupTable lookup_table,
GX_LookupTable_Funcs funcs,
FT_Pointer user );
static FT_Error
gx_load_BinSrchHeader( FT_Stream stream,
GX_BinSrchHeader header )
{
FT_Error error;
static const FT_Frame_Field fields[] =
{
#undef FT_STRUCTURE
#define FT_STRUCTURE GX_BinSrchHeaderRec
FT_FRAME_START( 10 ),
FT_FRAME_USHORT (unitSize),
FT_FRAME_USHORT (nUnits),
FT_FRAME_USHORT (searchRange),
FT_FRAME_USHORT (entrySelector),
FT_FRAME_USHORT (rangeShift),
FT_FRAME_END
};
FT_STREAM_READ_FIELDS( fields, header );
return error;
}
static FT_Error
gx_LookupTable_load_raw_values ( FT_Stream stream,
FT_Long value_count,
GX_LookupValue value_slot )
{
FT_Error error;
FT_Long i;
for ( i = 0; i < value_count; i++)
{
value_slot[i].extra.any = NULL;
if ( FT_READ_SHORT(value_slot[i].raw.s) )
return error;
}
return error;
}
static FT_Error
gx_LookupTable_load_segment_generic( GX_LookupTable lookup_table,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
GX_LookupTable_Segment segment_table = lookup_table->fsHeader.segment_generic;
GX_BinSrchHeaderRec binSrchHeader = segment_table->binSrchHeader;
GX_LookupSegment segment;
FT_Long i;
if ( FT_NEW_ARRAY( segment, binSrchHeader.nUnits ) )
return error;
for ( i = 0; i < binSrchHeader.nUnits; i++ )
{
if ( FT_READ_USHORT(segment[i].lastGlyph) ||
FT_READ_USHORT(segment[i].firstGlyph) )
goto Failure;
error = gx_LookupTable_load_raw_values(stream, 1, &(segment[i].value));
if ( error )
goto Failure;
}
segment_table->segments = segment;
return error;
Failure:
FT_FREE(segment);
return error;
}
static void
gx_LookupTable_free_segment_generic( FT_Memory memory,
GX_LookupTable lookup_table )
{
GX_LookupTable_Segment segment_table = lookup_table->fsHeader.segment_generic;
FT_FREE(segment_table->segments);
segment_table->segments = NULL;
}
static FT_Error
gx_LookupTable_load_single_table( GX_LookupTable lookup_table,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
GX_LookupTable_Single_Table single_table = lookup_table->fsHeader.single_table;
GX_BinSrchHeaderRec binSrchHeader = single_table->binSrchHeader;
GX_LookupSingle single;
FT_Long i;
if ( FT_NEW_ARRAY( single, binSrchHeader.nUnits ) )
return error;
for ( i = 0; i < binSrchHeader.nUnits; i++ )
{
if ( FT_READ_USHORT(single[i].glyph) )
goto Failure;
gx_LookupTable_load_raw_values(stream, 1, &(single[i].value));
if ( error )
goto Failure;
}
single_table->entries = single;
return error;
Failure:
FT_FREE(single);
single_table->entries = NULL;
return error;
}
static void
gx_LookupTable_free_single_table( FT_Memory memory,
GX_LookupTable lookup_table )
{
GX_LookupTable_Single_Table single_table = lookup_table->fsHeader.single_table;
FT_FREE(single_table->entries);
single_table->entries = NULL;
}
static FT_Error
gx_LookupTable_load_binSrch( GX_LookupTable lookup_table,
FT_Stream stream )
{
FT_Error error = GX_Err_Ok;
FT_Memory memory = stream->memory;
GX_LookupTable_BinSrch binSrch;
gx_LookupTable_loader lookuptable_loader;
switch (lookup_table->format)
{
case GX_LOOKUPTABLE_SEGMENT_SINGLE:
case GX_LOOKUPTABLE_SEGMENT_ARRAY:
lookuptable_loader = gx_LookupTable_load_segment_generic;
break;
case GX_LOOKUPTABLE_SINGLE_TABLE:
lookuptable_loader = gx_LookupTable_load_single_table;
break;
default:
return GX_Err_Invalid_Table;
}
if ( FT_MEM_NEW (binSrch) )
return error;
binSrch->dummy = NULL;
error = gx_load_BinSrchHeader( stream, &(binSrch->binSrchHeader) );
if ( error )
goto Failure;
lookup_table->fsHeader.bin_srch = binSrch;
error = lookuptable_loader ( lookup_table, stream );
if ( error )
{
lookup_table->fsHeader.bin_srch = NULL;
goto Failure;
}
return error;
Failure:
FT_FREE ( binSrch );
return error;
}
static void
gx_LookupTable_free_binSrch ( FT_Memory memory,
GX_LookupTable lookup_table )
{
GX_LookupTable_BinSrch binSrch = lookup_table->fsHeader.bin_srch;
gx_LookupTable_finalizer finalizer;
switch (lookup_table->format)
{
case GX_LOOKUPTABLE_SEGMENT_SINGLE:
case GX_LOOKUPTABLE_SEGMENT_ARRAY:
finalizer = gx_LookupTable_free_segment_generic;
break;
case GX_LOOKUPTABLE_SINGLE_TABLE:
finalizer = gx_LookupTable_free_single_table;
break;
default:
finalizer = NULL;
}
FT_ASSERT(finalizer);
finalizer ( memory, lookup_table );
binSrch->dummy = NULL;
FT_FREE ( lookup_table->fsHeader.bin_srch );
lookup_table->fsHeader.bin_srch = NULL;
}
static FT_Error
gx_LookupTable_load_simple_array( GX_LookupTable lookup_table,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
GX_LookupValue value_slot;
if ( FT_NEW_ARRAY (value_slot, lookup_table->num_glyphs) )
return error;
error = gx_LookupTable_load_raw_values ( stream,
lookup_table->num_glyphs,
value_slot );
if ( error )
goto Failure;
lookup_table->fsHeader.simple_array = value_slot;
return error;
Failure:
FT_FREE( value_slot );
return error;
}
static void
gx_LookupTable_free_simple_array( FT_Memory memory,
GX_LookupTable lookup_table )
{
FT_FREE ( lookup_table->fsHeader.simple_array );
lookup_table->fsHeader.simple_array = NULL;
}
static FT_Error
gx_LookupTable_load_trimmed_array( GX_LookupTable lookup_table,
FT_Stream stream )
{
FT_Error error;
FT_Memory memory = stream->memory;
FT_Short firstGlyph, glyphCount;
GX_LookupTable_Trimmed_Array trimmed_array;
GX_LookupValue value_slot;
if ( FT_READ_USHORT(firstGlyph) ||
FT_READ_USHORT(glyphCount) )
return error;
if ( FT_ALLOC (trimmed_array,
sizeof (*trimmed_array) + sizeof(*trimmed_array->valueArray) * glyphCount) )
return error;
trimmed_array->firstGlyph = firstGlyph;
trimmed_array->glyphCount = glyphCount;
trimmed_array->valueArray = NULL;
value_slot =(GX_LookupValue)(((char *)trimmed_array) + sizeof (*trimmed_array));
error = gx_LookupTable_load_raw_values ( stream, glyphCount, value_slot );
if ( error )
goto Failure;
lookup_table->fsHeader.trimmed_array = trimmed_array;
trimmed_array->valueArray = value_slot;
return error;
Failure:
FT_FREE( trimmed_array );
lookup_table->fsHeader.trimmed_array = NULL;
return error;
}
static void
gx_LookupTable_free_trimmed_array( FT_Memory memory,
GX_LookupTable lookup_table )
{
GX_LookupTable_Trimmed_Array trimmed_array;
trimmed_array = lookup_table->fsHeader.trimmed_array;
trimmed_array->valueArray = NULL;
FT_FREE(trimmed_array);
lookup_table->fsHeader.trimmed_array = NULL;
}
FT_LOCAL_DEF( FT_Error )
gx_face_load_LookupTable ( GX_Face face,
FT_Stream stream,
GX_LookupTable lookup_table )
{
FT_Error error;
gx_LookupTable_loader loader;
lookup_table->position = FT_STREAM_POS();
lookup_table->num_glyphs = face->root.num_glyphs;
lookup_table->fsHeader.any = NULL;
if ( FT_READ_SHORT(lookup_table->format) )
return error;
switch ( lookup_table->format )
{
case GX_LOOKUPTABLE_SIMPLE_ARRAY:
loader = gx_LookupTable_load_simple_array;
break;
case GX_LOOKUPTABLE_TRIMMED_ARRAY:
loader = gx_LookupTable_load_trimmed_array;
break;
default:
loader = gx_LookupTable_load_binSrch;
}
error = (*loader)( lookup_table, stream );
return error;
}
FT_LOCAL_DEF ( void )
gx_LookupTable_free ( GX_LookupTable lookup_table,
FT_Memory memory )
{
gx_LookupTable_finalizer finalizer;
switch ( lookup_table->format )
{
case GX_LOOKUPTABLE_SIMPLE_ARRAY:
finalizer = gx_LookupTable_free_simple_array;
break;
case GX_LOOKUPTABLE_TRIMMED_ARRAY:
finalizer = gx_LookupTable_free_trimmed_array;
break;
default:
finalizer = gx_LookupTable_free_binSrch;
}
finalizer ( memory, lookup_table );
}
static FT_Error
gx_LookupTable_traverse_simple_array( GX_LookupTable lookup_table,
GX_LookupTable_Funcs funcs,
FT_Pointer user )
{
FT_Long i;
FT_Error error = GX_Err_Ok;
if ( funcs->simple_array_func )
{
for ( i = 0; i < lookup_table->num_glyphs; i++ )
{
error = (* funcs->simple_array_func)(lookup_table->format,
i,
&((lookup_table->fsHeader.simple_array)[i]),
user);
if ( error )
return error;
}
}
else if ( funcs->generic_func )
{
for ( i = 0; i < lookup_table->num_glyphs; i++ )
{
error = (* funcs->generic_func)(lookup_table->format,
&((lookup_table->fsHeader.simple_array)[i]),
user);
if ( error )
return error;
}
}
else
error = GX_Err_Ok;
return error;
}
static FT_Error
gx_LookupTable_traverse_segment_generic( GX_LookupTable lookup_table,
GX_LookupTable_Funcs funcs,
FT_Pointer user )
{
FT_Error error = GX_Err_Ok;
GX_LookupTable_Segment segment_table = lookup_table->fsHeader.segment_generic;
GX_BinSrchHeader header = &(segment_table->binSrchHeader);
GX_LookupSegment segment;
GX_LookupTable_Segment_Func segment_func;
FT_Long i;
if ( lookup_table->format == GX_LOOKUPTABLE_SEGMENT_SINGLE )
segment_func = funcs->segment_single_func;
else if ( lookup_table->format == GX_LOOKUPTABLE_SEGMENT_ARRAY)
segment_func = funcs->segment_array_func;
else
segment_func = NULL;
if ( segment_func )
{
for ( i = 0; i < header->nUnits; i++ )
{
segment = &(segment_table->segments[i]);
error = (*segment_func)( lookup_table->format,
segment->lastGlyph,
segment->firstGlyph,
&(segment->value),
user );
if ( error )
return error;
}
}
else if ( funcs->generic_func )
{
for ( i = 0; i < header->nUnits; i++ )
{
segment = &(segment_table->segments[i]);
error = (*funcs->generic_func)( lookup_table->format,
&(segment->value),
user );
if ( error )
return error;
}
}
else
error = GX_Err_Ok;
return error;
}
static FT_Error
gx_LookupTable_traverse_single_table ( GX_LookupTable lookup_table,
GX_LookupTable_Funcs funcs,
FT_Pointer user )
{
FT_Error error = GX_Err_Ok;
GX_LookupTable_Single_Table single_table;
GX_LookupSingle entries;
GX_BinSrchHeader binSrchHeader;
FT_Long i;
single_table = lookup_table->fsHeader.single_table;
entries = single_table->entries;
binSrchHeader = &(single_table->binSrchHeader);
if ( funcs->single_table_func )
{
for ( i = 0; i < binSrchHeader->nUnits; i++ )
{
error = (*funcs->single_table_func) ( lookup_table->format,
entries[i].glyph,
&(entries[i].value),
user );
if ( error )
return error;
}
}
else if ( funcs->generic_func )
{
for ( i = 0; i < binSrchHeader->nUnits; i++ )
{
error = (* funcs->generic_func) ( lookup_table->format,
&(entries[i].value),
user );
if ( error )
return error;
}
}
else
error = GX_Err_Ok;
return error;
}
static FT_Error
gx_LookupTable_traverse_trimmed_array( GX_LookupTable lookup_table,
GX_LookupTable_Funcs funcs,
FT_Pointer user )
{
FT_Error error = GX_Err_Ok;
FT_Long i;
GX_LookupTable_Trimmed_Array trimmed_array = lookup_table->fsHeader.trimmed_array;
if ( funcs->trimmed_array_func )
{
for ( i = 0; i < trimmed_array->glyphCount; i++ )
{
error = (* funcs->trimmed_array_func)(lookup_table->format,
i,
trimmed_array->firstGlyph,
trimmed_array->glyphCount,
&(trimmed_array->valueArray[i]),
user);
if ( error )
return error;
}
}
else if ( funcs->generic_func )
{
for ( i = 0; i < trimmed_array->glyphCount; i++ )
{
error = (* funcs->generic_func)(lookup_table->format,
&(trimmed_array->valueArray[i]),
user);
if ( error )
return error;
}
}
else
error = GX_Err_Ok;
return error;
}
FT_LOCAL_DEF ( FT_Error )
gx_LookupTable_traverse_low( GX_LookupTable lookup_table,
GX_LookupTable_Funcs funcs,
FT_Pointer user )
{
gx_LookupTable_traverser traverser;
switch (lookup_table->format)
{
case GX_LOOKUPTABLE_SIMPLE_ARRAY:
traverser = gx_LookupTable_traverse_simple_array;
break;
case GX_LOOKUPTABLE_SEGMENT_SINGLE:
case GX_LOOKUPTABLE_SEGMENT_ARRAY:
traverser = gx_LookupTable_traverse_segment_generic;
break;
case GX_LOOKUPTABLE_SINGLE_TABLE:
traverser = gx_LookupTable_traverse_single_table;
break;
case GX_LOOKUPTABLE_TRIMMED_ARRAY:
traverser = gx_LookupTable_traverse_trimmed_array;
break;
default:
traverser = NULL;
}
FT_ASSERT(traverser);
return (*traverser) ( lookup_table, funcs, user );
}
static int
lookup_lookup_segment (const void *keyval, const void *datum)
{
const GX_LookupSegment segment = (const GX_LookupSegment)datum;
FT_UShort glyph = *(FT_UShort*)keyval;
if ( glyph < segment->firstGlyph )
return -1;
else if ( segment->lastGlyph < glyph )
return 1;
else
return 0;
}
static int
lookup_lookup_single(const void *keyval, const void *datum)
{
const GX_LookupSingle entry = (const GX_LookupSingle)datum;
FT_UShort glyph = *(FT_UShort*)keyval;
if ( glyph < entry->glyph )
return -1;
else if ( entry->glyph < glyph )
return 1;
else
return 0;
}
FT_LOCAL_DEF( GX_LookupResultRec )
gx_LookupTable_lookup ( GX_LookupTable lookup_table,
FT_UShort glyph )
{
GX_LookupResultRec result;
GX_LookupTable_Segment segment_table;
GX_LookupSegment segment;
GX_LookupTable_Single_Table single_table;
GX_LookupSingle entry;
GX_LookupTable_Trimmed_Array trimmed_array;
FT_Long trimmed_index;
void * bs_key = &glyph;
void * bs_base;
size_t bs_n;
size_t bs_size;
int (* bs_cmp)(const void* keyval, const void* datum);
result.firstGlyph = GX_LOOKUP_RESULT_NO_FIRST_GLYPH;
result.value = NULL;
switch ( lookup_table->format )
{
case GX_LOOKUPTABLE_SIMPLE_ARRAY:
if ( glyph < lookup_table->num_glyphs )
result.value = &((lookup_table->fsHeader.simple_array) [glyph]);
break;
case GX_LOOKUPTABLE_SEGMENT_SINGLE:
case GX_LOOKUPTABLE_SEGMENT_ARRAY:
segment_table = lookup_table->fsHeader.segment_generic;
bs_base = segment_table->segments;
bs_n = segment_table->binSrchHeader.nUnits;
bs_size = sizeof (*segment_table->segments);
bs_cmp = lookup_lookup_segment;
segment = ft_bsearch( bs_key, bs_base, bs_n, bs_size, bs_cmp );
if ( segment )
{
result.value = &segment->value;
if ( ( lookup_table->format == GX_LOOKUPTABLE_SEGMENT_ARRAY ) )
result.firstGlyph = segment->firstGlyph;
}
return result;
case GX_LOOKUPTABLE_SINGLE_TABLE:
single_table = lookup_table->fsHeader.single_table;
bs_base = single_table->entries;
bs_n = single_table->binSrchHeader.nUnits;
bs_size = sizeof (*single_table->entries);
bs_cmp = lookup_lookup_single;
entry = ft_bsearch( bs_key, bs_base, bs_n, bs_size, bs_cmp );
if ( entry )
result.value = &(entry->value);
break;
case GX_LOOKUPTABLE_TRIMMED_ARRAY:
trimmed_array = lookup_table->fsHeader.trimmed_array;
if ( glyph < trimmed_array->firstGlyph )
break;
trimmed_index = glyph - trimmed_array->firstGlyph;
if ( trimmed_index < trimmed_array->glyphCount )
result.value = &(trimmed_array->valueArray[trimmed_index]);
break;
}
return result;
}
FT_LOCAL ( FT_Error )
gx_LookupTable_traverse_high ( GX_LookupTable lookup_table,
GX_LookupTable_Glyph_Func func,
FT_Pointer user)
{
FT_Error error;
FT_UShort glyph;
GX_LookupResultRec result;
for ( glyph = 0; glyph < 0xFFFF; glyph++ )
{
result = gx_LookupTable_lookup ( lookup_table, glyph );
if ( result.value == NULL)
continue ;
if (( error = func( glyph, result.value, result.firstGlyph, user ) ))
return error;
}
return FT_Err_Ok;
}
/* END */