[autofit] Implement vertical separation adjustment.

* src/autofit/aflatin.c: Include `afadjust.h`.
(af_latin_metrics_init): Call `af_reverse_character_map_new`.
(af_latin_metrics_done): New function.

(af_move_contour_vertically, af_check_contour_horizontal_overlap,
af_glyph_hints_apply_vertical_separation_adjustments): New functions.

(af_latin_hints_apply): Call
`af_glyph_hints_apply_vertical_separation_adjustments`.

(af_latin_writing_system_class): Updated.

* src/autofit/aftypes.h (AF_StyleMetricsRec): Add `reverse_charmap` field.
This commit is contained in:
Craig White 2024-01-29 08:21:09 +01:00 committed by Werner Lemberg
parent 2d84675f4b
commit 34c1acfca3
2 changed files with 321 additions and 1 deletions

View File

@ -22,6 +22,7 @@
#include "afglobal.h"
#include "aflatin.h"
#include "aferrors.h"
#include "afadjust.h"
/**************************************************************************
@ -1155,6 +1156,9 @@
af_latin_metrics_check_digits( metrics, face );
}
af_reverse_character_map_new( &metrics->root.reverse_charmap,
metrics->root.globals );
Exit:
face->charmap = oldmap;
return error;
@ -1485,6 +1489,17 @@
}
FT_CALLBACK_DEF( void )
af_latin_metrics_done( AF_StyleMetrics metrics_ )
{
AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
af_reverse_character_map_done( metrics->root.reverse_charmap,
metrics->root.globals->face->memory );
}
/* Scale global values in both directions. */
FT_LOCAL_DEF( void )
@ -2738,6 +2753,304 @@
}
#undef FT_COMPONENT
#define FT_COMPONENT afadjust
static void
af_move_contour_vertically( AF_Point contour,
FT_Int movement )
{
AF_Point point = contour;
AF_Point first_point = point;
if ( point )
{
do
{
point->y += movement;
point = point->next;
} while ( point != first_point );
}
}
/* Return 1 if the given contour overlaps horizontally with the bounding */
/* box of all other contours combined. This is a helper for function */
/* `af_glyph_hints_apply_vertical_separation_adjustments`. */
static FT_Bool
af_check_contour_horizontal_overlap( AF_GlyphHints hints,
FT_Int contour_index )
{
FT_Pos contour_max_x = -32000;
FT_Pos contour_min_x = 32000;
FT_Pos others_max_x = -32000;
FT_Pos others_min_x = 32000;
FT_Int contour;
FT_Bool horizontal_overlap;
for ( contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point first_point = hints->contours[contour];
AF_Point p = first_point;
do
{
p = p->next;
if ( contour == contour_index )
{
if ( p->x < contour_min_x )
contour_min_x = p->x;
if ( p->x > contour_max_x )
contour_max_x = p->x;
}
else
{
if ( p->x < others_min_x )
others_min_x = p->x;
if ( p->x > others_max_x )
others_max_x = p->x;
}
} while ( p != first_point );
}
horizontal_overlap =
( others_min_x <= contour_max_x && contour_max_x <= others_max_x ) ||
( others_min_x <= contour_min_x && contour_min_x <= others_max_x ) ||
( contour_max_x >= others_max_x && contour_min_x <= others_min_x );
return horizontal_overlap;
}
static void
af_glyph_hints_apply_vertical_separation_adjustments(
AF_GlyphHints hints,
AF_Dimension dim,
FT_Int glyph_index,
AF_ReverseCharacterMap reverse_charmap )
{
FT_TRACE4(( "Entering"
" af_glyph_hints_apply_vertical_separation_adjustments\n" ));
if ( dim != AF_DIMENSION_VERT )
return;
if ( af_lookup_vertical_separation_type( reverse_charmap,
glyph_index ) ==
AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP &&
hints->num_contours >= 2 )
{
FT_Int highest_contour = -1;
FT_Pos highest_min_y = 0;
FT_Pos current_min_y = 0;
FT_Int contour;
FT_Bool horizontal_overlap;
FT_Int adjustment_amount;
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n"
" Applying vertical adjustment:"
" AF_VERTICAL_ADJUSTMENT_TOP_CONTOUR_UP\n" ));
/* Figure out which contour is the higher one by finding the one */
/* with the highest minimum y value. */
for ( contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point point = hints->contours[contour];
AF_Point first_point = point;
if ( !point )
continue;
current_min_y = point->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 )
{
highest_min_y = current_min_y;
highest_contour = contour;
}
}
/* Check for a horizontal overlap between the top contour and the */
/* rest. If there is no overlap, do not adjust. */
horizontal_overlap =
af_check_contour_horizontal_overlap( hints, highest_contour );
if ( !horizontal_overlap )
{
FT_TRACE4(( " Top contour does not horizontally overlap"
" with other contours.\n"
" Skipping adjustment.\n" ));
return;
}
/* If there are any contours that have a maximum y coordinate */
/* greater than or equal to the minimum y coordinate of the */
/* previously found highest contour, bump the high contour up until */
/* the distance is one pixel. */
adjustment_amount = 0;
for ( contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point point;
AF_Point first_point;
FT_Pos max_y;
if ( contour == highest_contour )
continue;
point = hints->contours[contour];
first_point = point;
if ( !point )
continue;
max_y = point->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 ( adjustment_amount > 64 )
FT_TRACE4(( " Calculated adjustment amount %d"
" was more than threshold of 64. Not adjusting\n",
adjustment_amount ));
else if ( adjustment_amount > 0 )
{
FT_TRACE4(( " Pushing top contour %d units up\n",
adjustment_amount ));
af_move_contour_vertically( hints->contours[highest_contour],
adjustment_amount );
}
}
else if ( af_lookup_vertical_separation_type( reverse_charmap,
glyph_index ) ==
AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN &&
hints->num_contours >= 2 )
{
FT_Int lowest_contour = -1;
FT_Pos lowest_max_y = 0;
FT_Pos current_max_y = 0;
FT_Int contour;
FT_Int adjustment_amount;
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n"
" Applying vertical adjustment:"
" AF_VERTICAL_ADJUSTMENT_BOTTOM_CONTOUR_DOWN\n" ));
/* Find lowest contour. */
for ( contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point point = hints->contours[contour];
AF_Point first_point = point;
if ( !point )
continue;
current_max_y = point->y;
do
{
if ( point->y > current_max_y )
current_max_y = point->y;
point = point->next;
} while ( point != first_point );
if ( lowest_contour == -1 || current_max_y < lowest_max_y )
{
lowest_max_y = current_max_y;
lowest_contour = contour;
}
}
adjustment_amount = 0;
for ( contour = 0; contour < hints->num_contours; contour++ )
{
AF_Point point;
AF_Point first_point;
FT_Pos min_y;
if ( contour == lowest_contour )
continue;
point = hints->contours[contour];
first_point = point;
if ( !point )
continue;
min_y = point->y;
do
{
if ( point->y < min_y )
min_y = point->y;
point = point->next;
} while ( point != first_point );
if ( min_y <= lowest_max_y - 64 )
adjustment_amount = 64 - ( min_y - lowest_max_y );
}
if ( adjustment_amount > 64 )
FT_TRACE4(( " Calculated adjustment amount %d"
" was more than threshold of 64. Not adjusting\n",
adjustment_amount ));
else if ( adjustment_amount > 0 )
{
FT_TRACE4(( " Pushing bottom contour %d units down\n",
adjustment_amount ));
af_move_contour_vertically( hints->contours[lowest_contour],
-adjustment_amount );
}
}
else
FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n"
" No vertical adjustment needed\n" ));
FT_TRACE4(( "Exiting"
" af_glyph_hints_apply_vertical_separation_adjustments\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 */
/* parameters influence the whole hinting process. */
@ -3605,6 +3918,11 @@
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_vertical_separation_adjustments(
hints,
(AF_Dimension)dim,
glyph_index,
metrics->root.reverse_charmap );
}
}
@ -3633,7 +3951,7 @@
(AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init, /* style_metrics_init */
(AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale, /* style_metrics_scale */
(AF_WritingSystem_DoneMetricsFunc) NULL, /* style_metrics_done */
(AF_WritingSystem_DoneMetricsFunc) af_latin_metrics_done, /* style_metrics_done */
(AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, /* style_metrics_getstdw */
(AF_WritingSystem_InitHintsFunc) af_latin_hints_init, /* style_hints_init */

View File

@ -439,6 +439,8 @@ extern void* af_debug_hints_;
AF_FaceGlobals globals; /* to access properties */
AF_ReverseCharacterMap reverse_charmap;
} AF_StyleMetricsRec;