* Add trace output
* Remove assumption of exactly 2 contours * Adjustment database lookups use binary search now
This commit is contained in:
parent
f8e996bfb1
commit
62378cca3a
|
@ -164,6 +164,7 @@ FT_TRACE_DEF( afhints )
|
|||
FT_TRACE_DEF( afmodule )
|
||||
FT_TRACE_DEF( aflatin )
|
||||
FT_TRACE_DEF( afshaper )
|
||||
FT_TRACE_DEF( afadjust )
|
||||
|
||||
/* SDF components */
|
||||
FT_TRACE_DEF( sdf ) /* signed distance raster for outlines (ftsdf.c) */
|
||||
|
|
|
@ -2,34 +2,52 @@
|
|||
#include <freetype/freetype.h>
|
||||
#include <freetype/internal/ftobjs.h>
|
||||
#include <freetype/internal/ftmemory.h>
|
||||
#include <freetype/internal/ftdebug.h>
|
||||
|
||||
#define AF_ADJUSTMENT_DATABASE_LENGTH 12
|
||||
#define AF_ADJUSTMENT_DATABASE_LENGTH (sizeof(adjustment_database)/sizeof(adjustment_database[0]))
|
||||
#undef FT_COMPONENT
|
||||
#define FT_COMPONENT afadjust
|
||||
|
||||
/*TODO: find out whether capital u/U with accent entries are needed*/
|
||||
/*the accent won't merge with the rest of the glyph because the accent mark is sitting above empty space*/
|
||||
FT_LOCAL_ARRAY_DEF( AF_AdjustmentDatabaseEntry )
|
||||
adjustment_database[AF_ADJUSTMENT_DATABASE_LENGTH] = {
|
||||
{'i', AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE},
|
||||
{'j', AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE},
|
||||
{0xC8, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*E with grave*/
|
||||
{0xCC, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*I with grave*/
|
||||
{0xD9, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*U with grave*/
|
||||
{0xE0, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*a with grave*/
|
||||
{0xEC, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*i with grave*/
|
||||
{0x114, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*E with macron*/
|
||||
{0x12A, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*I with macron*/
|
||||
{0x12B, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*i with macron*/
|
||||
{0x16A, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE}, /*U with macron*/
|
||||
{0x16B, AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE} /*u with macron*/
|
||||
/*TODO: find out why E won't work, even though it appears to be one-on-one*/
|
||||
adjustment_database[] =
|
||||
{
|
||||
{0x21, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /* ! */
|
||||
{0x69, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /* i */
|
||||
{0x6A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /* j */
|
||||
{0xA1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*Inverted Exclamation Mark*/
|
||||
{0xBF, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*Inverted Question Mark*/
|
||||
{0xC0, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*A with grave*/
|
||||
{0xC1, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*A with acute*/
|
||||
{0xC2, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*A with circumflex*/
|
||||
{0xC3, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*A with tilde*/
|
||||
{0xC8, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*E with grave*/
|
||||
{0xCC, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*I with grave*/
|
||||
{0xD9, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*U with grave*/
|
||||
{0xE0, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*a with grave*/
|
||||
{0xEC, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*i with grave*/
|
||||
{0x114, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*E with macron*/
|
||||
{0x12A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*I with macron*/
|
||||
{0x12B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*i with macron*/
|
||||
{0x16A, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP}, /*U with macron*/
|
||||
{0x16B, AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP} /*u with macron*/
|
||||
};
|
||||
|
||||
/*Helper function: get the adjustment database entry for a codepoint*/
|
||||
FT_LOCAL_DEF( const AF_AdjustmentDatabaseEntry* )
|
||||
af_adjustment_database_lookup( FT_UInt32 codepoint ) {
|
||||
for ( FT_Int entry = 0; entry < AF_ADJUSTMENT_DATABASE_LENGTH; entry++ ) {
|
||||
if ( adjustment_database[entry].codepoint == codepoint ) {
|
||||
return &adjustment_database[entry];
|
||||
/* Binary search for database entry */
|
||||
FT_UInt low = 0;
|
||||
FT_UInt high = AF_ADJUSTMENT_DATABASE_LENGTH - 1;
|
||||
while (high > low) {
|
||||
FT_UInt mid = (low + high) / 2;
|
||||
if (adjustment_database[mid].codepoint < codepoint) {
|
||||
low = mid + 1;
|
||||
} else if (adjustment_database[mid].codepoint > codepoint) {
|
||||
high = mid - 1;
|
||||
} else {
|
||||
return &adjustment_database[mid];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,30 +58,37 @@ FT_LOCAL_DEF( AF_VerticalSeparationAdjustmentType )
|
|||
af_lookup_vertical_seperation_type( AF_ReverseCharacterMap map, FT_Int glyph_index ) {
|
||||
FT_UInt32 codepoint = af_reverse_character_map_lookup( map, glyph_index );
|
||||
const AF_AdjustmentDatabaseEntry *entry = af_adjustment_database_lookup( codepoint );
|
||||
if ( entry == NULL ) {
|
||||
if ( entry == NULL )
|
||||
{
|
||||
return AF_VERTICAL_ADJUSTMENT_NONE;
|
||||
}
|
||||
return entry->vertical_separation_adjustment_type;
|
||||
}
|
||||
|
||||
typedef struct AF_ReverseMapEntry_ {
|
||||
typedef struct AF_ReverseMapEntry_
|
||||
{
|
||||
FT_Int glyph_index;
|
||||
FT_UInt32 codepoint;
|
||||
} AF_ReverseMapEntry;
|
||||
|
||||
typedef struct AF_ReverseCharacterMap_ {
|
||||
typedef struct AF_ReverseCharacterMap_
|
||||
{
|
||||
FT_UInt length;
|
||||
AF_ReverseMapEntry *entries;
|
||||
} AF_ReverseCharacterMap_Rec;
|
||||
|
||||
FT_LOCAL_DEF(FT_UInt32)
|
||||
af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index ) {
|
||||
if ( map == NULL ) {
|
||||
af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index )
|
||||
{
|
||||
if ( map == NULL )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for ( FT_UInt entry = 0; entry < map->length; entry++ ) {
|
||||
if ( map->entries[entry].glyph_index == glyph_index ) {
|
||||
for ( FT_UInt entry = 0; entry < map->length; entry++ )
|
||||
{
|
||||
if ( map->entries[entry].glyph_index == glyph_index )
|
||||
{
|
||||
return map->entries[entry].codepoint;
|
||||
}
|
||||
}
|
||||
|
@ -72,64 +97,87 @@ af_reverse_character_map_lookup( AF_ReverseCharacterMap map, FT_Int glyph_index
|
|||
}
|
||||
|
||||
FT_LOCAL_DEF( FT_Error )
|
||||
af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memory memory ) {
|
||||
af_reverse_character_map_new( FT_Face face, AF_ReverseCharacterMap *map, FT_Memory memory )
|
||||
{
|
||||
/* Search for a unicode charmap */
|
||||
/* If there isn't one, create a blank map */
|
||||
|
||||
/*TODO: change this to logic that searches for a "preferred" unicode charmap that maps the most codepoints*/
|
||||
/*see find_unicode_charmap*/
|
||||
/*TODO: use GSUB lookups */
|
||||
FT_TRACE4(( "af_reverse_character_map_new: building reverse character map\n" ));
|
||||
FT_CMap unicode_charmap = NULL;
|
||||
for ( FT_UInt i = 0; i < face->num_charmaps; i++ ) {
|
||||
if ( face->charmaps[i]->encoding == FT_ENCODING_UNICODE ) {
|
||||
for ( FT_UInt i = 0; i < face->num_charmaps; i++ )
|
||||
{
|
||||
if ( face->charmaps[i]->encoding == FT_ENCODING_UNICODE )
|
||||
{
|
||||
unicode_charmap = FT_CMAP( face->charmaps[i] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( unicode_charmap == NULL ) {
|
||||
if ( unicode_charmap == NULL )
|
||||
{
|
||||
*map = NULL;
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
FT_Error error;
|
||||
|
||||
if ( FT_NEW( *map ) ) {
|
||||
if ( FT_NEW( *map ) )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
FT_Int capacity = 10;
|
||||
FT_Int size = 0;
|
||||
|
||||
if ( FT_NEW_ARRAY((*map)->entries, capacity) ) {
|
||||
if ( FT_NEW_ARRAY((*map)->entries, capacity) )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
for ( FT_Int i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ ) {
|
||||
#ifdef FT_DEBUG_LEVEL_TRACE
|
||||
int failed_lookups = 0;
|
||||
#endif
|
||||
for ( FT_Int i = 0; i < AF_ADJUSTMENT_DATABASE_LENGTH; i++ )
|
||||
{
|
||||
FT_UInt32 codepoint = adjustment_database[i].codepoint;
|
||||
FT_Int glyph = unicode_charmap->clazz->char_index(unicode_charmap, codepoint);
|
||||
if ( glyph == 0 ) {
|
||||
if ( glyph == 0 )
|
||||
{
|
||||
#ifdef FT_DEBUG_LEVEL_TRACE
|
||||
failed_lookups++;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
if (size == capacity) {
|
||||
if ( size == capacity )
|
||||
{
|
||||
capacity += capacity / 2;
|
||||
if ( FT_RENEW_ARRAY((*map)->entries, size, capacity) ) {
|
||||
if ( FT_RENEW_ARRAY((*map)->entries, size, capacity) )
|
||||
{
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
size++;
|
||||
(*map)->entries[i].glyph_index = glyph;
|
||||
(*map)->entries[i].codepoint = codepoint;
|
||||
( *map )->entries[i].glyph_index = glyph;
|
||||
( *map )->entries[i].codepoint = codepoint;
|
||||
}
|
||||
(*map)->length = size;
|
||||
( *map )->length = size;
|
||||
|
||||
Exit:
|
||||
if ( error ) {
|
||||
if ( *map ) {
|
||||
if ( error )
|
||||
{
|
||||
FT_TRACE4(( " error while building reverse character map. Using blank map.\n" ));
|
||||
if ( *map )
|
||||
{
|
||||
FT_FREE( ( *map )->entries );
|
||||
}
|
||||
FT_FREE( *map );
|
||||
return error;
|
||||
}
|
||||
|
||||
#ifdef FT_DEBUG_LEVEL_TRACE
|
||||
FT_TRACE4(( " reverse character map built successfully"\
|
||||
" with %d entries and %d failed lookups.\n", size, failed_lookups ));
|
||||
#endif
|
||||
return FT_Err_Ok;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ FT_BEGIN_HEADER
|
|||
|
||||
/*The type of adjustment that should be done to prevent cases where 2 parts of a character*/
|
||||
/*stacked vertically merge, even though they should be separate*/
|
||||
typedef enum AF_VerticalSeparationAdjustmentType_ {
|
||||
AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE,
|
||||
/*"One on one" means that the character is expected to be one contour on top of another, where the contours should not touch*/
|
||||
/*the hinter will force the contours to have a gap of at least 1 pixel between them*/
|
||||
/*by moving the top contour up */
|
||||
typedef enum AF_VerticalSeparationAdjustmentType_
|
||||
{
|
||||
AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP,
|
||||
/*This means that the hinter should find the topmost contour and push it up until its lowest point is 1 pixel*/
|
||||
/*above the highest point not part of that contour.*/
|
||||
AF_VERTICAL_ADJUSTMENT_NONE
|
||||
|
||||
/*others will be needed, such as the case where the lower contour should be moved in the adjustment instead of the upper one*/
|
||||
|
@ -19,10 +19,11 @@ typedef enum AF_VerticalSeparationAdjustmentType_ {
|
|||
/*and a way of handling A and O, where the letter consists of 2 contours*/
|
||||
} AF_VerticalSeparationAdjustmentType;
|
||||
|
||||
typedef struct AF_AdjustmentDatabaseEntry_ {
|
||||
FT_UInt32 codepoint;
|
||||
AF_VerticalSeparationAdjustmentType vertical_separation_adjustment_type;
|
||||
} AF_AdjustmentDatabaseEntry;
|
||||
typedef struct AF_AdjustmentDatabaseEntry_
|
||||
{
|
||||
FT_UInt32 codepoint;
|
||||
AF_VerticalSeparationAdjustmentType vertical_separation_adjustment_type;
|
||||
} AF_AdjustmentDatabaseEntry;
|
||||
|
||||
struct AF_ReverseCharacterMap_;
|
||||
|
||||
|
|
|
@ -2745,12 +2745,24 @@
|
|||
}
|
||||
|
||||
|
||||
void af_glyph_hints_apply_adjustments(AF_GlyphHints hints, AF_Dimension dim, FT_Int glyph_index, AF_ReverseCharacterMap reverse_charmap) {
|
||||
if ( dim != AF_DIMENSION_VERT ) {
|
||||
#undef FT_COMPONENT
|
||||
#define FT_COMPONENT afadjust
|
||||
|
||||
void
|
||||
af_glyph_hints_apply_vertical_separation_adjustments( AF_GlyphHints hints,
|
||||
AF_Dimension dim,
|
||||
FT_Int glyph_index,
|
||||
AF_ReverseCharacterMap reverse_charmap )
|
||||
{
|
||||
if ( dim != AF_DIMENSION_VERT )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( af_lookup_vertical_seperation_type( reverse_charmap, glyph_index ) == AF_VERTICAL_ADJUSTMENT_ONE_ON_ONE &&
|
||||
hints->num_contours == 2 ) {
|
||||
|
||||
if ( af_lookup_vertical_seperation_type( reverse_charmap, glyph_index ) == AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP
|
||||
&& hints->num_contours >= 2 )
|
||||
{
|
||||
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments: Applying vertical adjustment: AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP\n" ));
|
||||
|
||||
/* Figure out which contout is the higher one by finding the one */
|
||||
/* with the highest minimum y value */
|
||||
|
@ -2759,22 +2771,27 @@ void af_glyph_hints_apply_adjustments(AF_GlyphHints hints, AF_Dimension dim, FT_
|
|||
FT_Pos highest_min_y = 0;
|
||||
FT_Pos current_min_y = 0;
|
||||
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ ) {
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
if ( point == NULL ) { /*TODO: is this necessary?*/
|
||||
if ( point == NULL )
|
||||
{ /*TODO: is this necessary?*/
|
||||
continue;
|
||||
}
|
||||
current_min_y = point->y;
|
||||
|
||||
do {
|
||||
if ( point->y < current_min_y ) {
|
||||
do
|
||||
{
|
||||
if ( point->y < current_min_y )
|
||||
{
|
||||
current_min_y = point->y;
|
||||
}
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
|
||||
if ( highest_contour == -1 || current_min_y > highest_min_y ) {
|
||||
if ( highest_contour == -1 || current_min_y > highest_min_y )
|
||||
{
|
||||
highest_min_y = current_min_y;
|
||||
highest_contour = contour;
|
||||
}
|
||||
|
@ -2785,42 +2802,57 @@ void af_glyph_hints_apply_adjustments(AF_GlyphHints hints, AF_Dimension dim, FT_
|
|||
/* contour, bump the high contour up until the distance is one pixel */
|
||||
|
||||
FT_Int adjustment_amount = 0;
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ ) {
|
||||
if (contour == highest_contour) {
|
||||
for ( FT_Int contour = 0; contour < hints->num_contours; contour++ )
|
||||
{
|
||||
if ( contour == highest_contour )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AF_Point point = hints->contours[contour];
|
||||
AF_Point first_point = point;
|
||||
if ( point == NULL ) {
|
||||
if ( point == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
FT_Pos max_y = point->y;
|
||||
|
||||
do {
|
||||
if ( point->y > max_y ) {
|
||||
do
|
||||
{
|
||||
if ( point->y > max_y )
|
||||
{
|
||||
max_y = point->y;
|
||||
}
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
|
||||
if ( max_y >= highest_min_y - 64 ) {
|
||||
adjustment_amount = 64 - (highest_min_y - max_y);
|
||||
if ( max_y >= highest_min_y - 64 )
|
||||
{
|
||||
adjustment_amount = 64 - ( highest_min_y - max_y );
|
||||
}
|
||||
}
|
||||
|
||||
FT_TRACE4(( " Pushing top contour %d units up\n", adjustment_amount ));
|
||||
if ( adjustment_amount > 0 ) {
|
||||
AF_Point point = hints->contours[highest_contour];
|
||||
AF_Point first_point = point;
|
||||
if ( point != NULL ) {
|
||||
do {
|
||||
if ( point != NULL )
|
||||
{
|
||||
do
|
||||
{
|
||||
point->y += adjustment_amount;
|
||||
point = point->next;
|
||||
} while ( point != first_point );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments: No vertical adjustment needed\n" ));
|
||||
}
|
||||
}
|
||||
|
||||
#undef FT_COMPONENT
|
||||
#define FT_COMPONENT aflatin
|
||||
|
||||
|
||||
|
||||
/* Compute the snapped width of a given stem, ignoring very thin ones. */
|
||||
/* There is a lot of voodoo in this function; changing the hard-coded */
|
||||
|
@ -3689,7 +3721,7 @@ void af_glyph_hints_apply_adjustments(AF_GlyphHints hints, AF_Dimension dim, FT_
|
|||
af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
|
||||
af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
|
||||
af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
|
||||
af_glyph_hints_apply_adjustments(hints, (AF_Dimension) dim, glyph_index, metrics->root.reverse_charmap);
|
||||
af_glyph_hints_apply_vertical_separation_adjustments(hints, (AF_Dimension) dim, glyph_index, metrics->root.reverse_charmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue