[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[freetype2] adjust adf5129ad 5/7: [autofit] Add tilde-unflattening algor
From: |
Werner Lemberg |
Subject: |
[freetype2] adjust adf5129ad 5/7: [autofit] Add tilde-unflattening algorithm. |
Date: |
Thu, 18 Jul 2024 03:58:29 -0400 (EDT) |
branch: adjust
commit adf5129ad949c5d76c39487df8275ed29f9f0736
Author: Craig White <gerzytet@gmail.com>
Commit: Werner Lemberg <wl@gnu.org>
[autofit] Add tilde-unflattening algorithm.
* src/autofit/aflatin.c (af_find_highest_contour,
af_remove_segments_containing_point,
af_latin_remove_tilde_points_from_edges, af_latin_stretch_tildes,
af_latin_align_tildes): New functions.
(af_latin_hints_apply): Call tilde-unflatting code if necessary.
---
src/autofit/aflatin.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 358 insertions(+)
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c
index c2e373455..4c9d15009 100644
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -2776,6 +2776,300 @@
}
+ static FT_Int
+ af_find_highest_contour( AF_GlyphHints hints )
+ {
+ FT_Int highest_contour = -1;
+
+ FT_Pos highest_min_y = 0;
+ FT_Pos current_min_y = 0;
+
+ FT_Int contour;
+
+
+ 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;
+ }
+ }
+
+ return highest_contour;
+ }
+
+
+ static void
+ af_remove_segments_containing_point( AF_GlyphHints hints,
+ AF_Point point )
+ {
+ AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT];
+ AF_Segment segments = axis->segments;
+
+ FT_UInt i;
+
+
+ for ( i = 0; i < axis->num_segments; i++ )
+ {
+ AF_Segment seg = &segments[i];
+ AF_Point p = seg->first;
+
+ FT_Bool remove = 0;
+
+
+ while ( 1 )
+ {
+ if ( p == point )
+ {
+ remove = 1;
+ break;
+ }
+
+ if ( p == seg->last )
+ break;
+
+ p = p->next;
+ }
+
+ if ( remove )
+ {
+ /* First, check the first and last segment of the edge. */
+ AF_Edge edge = seg->edge;
+
+
+ if ( edge->first == seg && edge->last == seg )
+ {
+ /* The edge only consists of the segment to be removed. */
+ /* Remove the edge. */
+ *edge = axis->edges[--axis->num_edges];
+ }
+ else
+ {
+ if ( edge->first == seg )
+ edge->first = seg->edge_next;
+
+ if ( edge->last == seg )
+ {
+ edge->last = edge->first;
+
+ while ( edge->last->edge_next != seg )
+ edge->last = edge->last->edge_next;
+ }
+ }
+
+ /* Now, delete the segment. */
+ *seg = axis->segments[--axis->num_segments];
+
+ i--; /* We have to check the new segment at this position. */
+ }
+ }
+ }
+
+
+ /* Remove all segments containing points on the tilde contour. */
+ static void
+ af_latin_remove_tilde_points_from_edges( AF_GlyphHints hints )
+ {
+ FT_Int highest_contour = af_find_highest_contour( hints );
+ AF_Point first_point = hints->contours[highest_contour];
+
+ AF_Point p = first_point;
+
+
+ do
+ {
+ p = p->next;
+ af_remove_segments_containing_point( hints, p );
+
+ } while ( p != first_point );
+ }
+
+
+ /*
+ The tilde-unflattening algorithm sometimes goes too far and makes an
+ unusually high tilde, where decreasing the ppem will increase the height
+ instead of a steady decrease in height as less pixels are used.
+
+ For example, the 'n tilde' glyph from 'Times New Roman', with forced
+ autofitting on, exhibits this behaviour in the font size range 16.5 to
+ 18 ppem.
+ */
+ static void
+ af_latin_stretch_tildes( AF_GlyphHints hints )
+ {
+ FT_Int highest_contour = af_find_highest_contour( hints );
+ AF_Point p = hints->contours[highest_contour];
+
+ AF_Point first_point = p;
+
+ FT_Pos min_y, max_y;
+ FT_Short min_fy, max_fy;
+
+ FT_Pos min_measurement;
+
+ FT_Pos height;
+ FT_Pos target_height;
+
+
+ min_y = max_y = p->y;
+ min_fy = max_fy = p->fy;
+
+ do
+ {
+ p = p->next;
+
+ if ( p->y < min_y )
+ min_y = p->y;
+ if ( p->y > max_y )
+ max_y = p->y;
+
+ if ( p->fy < min_fy )
+ min_fy = p->fy;
+ if ( p->fy > max_fy )
+ max_fy = p->fy;
+
+ } while ( p != first_point );
+
+ min_measurement = 32000;
+ do
+ {
+ p = p->next;
+
+ if ( !( p->flags & AF_FLAG_CONTROL ) &&
+ p->prev->y == p->y && p->next->y == p->y &&
+ p->y != min_y && p->y != max_y &&
+ p->prev->flags & AF_FLAG_CONTROL &&
+ p->next->flags & AF_FLAG_CONTROL )
+ {
+ /* This point could be a candidate. Find the next and previous */
+ /* on-curve points, and make sure they are both either above or */
+ /* below the point, then make the measurement. */
+ AF_Point prevOn = p->prev;
+ AF_Point nextOn = p->next;
+
+ FT_Pos measurement;
+
+
+ while ( prevOn->flags & AF_FLAG_CONTROL )
+ prevOn = prevOn->prev;
+ while ( nextOn->flags & AF_FLAG_CONTROL )
+ nextOn = nextOn->next;
+
+ if ( nextOn->y > p->y && prevOn->y > p->y )
+ measurement = p->y - min_y;
+ else if ( nextOn->y < p->y && prevOn->y < p->y )
+ measurement = max_y - p->y;
+ else
+ continue;
+
+ if ( measurement < min_measurement )
+ min_measurement = measurement;
+ }
+ } while ( p != first_point );
+
+ /* touch all points */
+ p = first_point;
+ do
+ {
+ p = p->next;
+ if ( !( p->flags & AF_FLAG_CONTROL ) )
+ p->flags |= AF_FLAG_TOUCH_Y;
+
+ } while ( p != first_point );
+
+ height = max_y - min_y;
+ target_height = min_measurement + 64;
+
+ if ( height >= target_height )
+ return;
+
+ p = first_point;
+ do
+ {
+ p = p->next;
+ p->y = ( ( p->y - min_y ) * target_height / height ) + min_y;
+ p->fy = ( ( p->fy - min_fy ) * target_height / height ) + min_fy;
+ p->oy = p->y;
+
+ } while ( p != first_point );
+ }
+
+
+ /*
+ As part of `af_latin_stretch_tildes`, all points in the tilde are marked
+ as touched, so the existing grid fitting will leave the tilde misaligned
+ with the grid.
+
+ This function moves the tilde contour down to be grid-fitted. We assume
+ that if moving the tilde down would cause it to touch or overlap another
+ countour, the vertical adjustment step will fix it.
+
+ Because the vertical adjustment step comes after all other grid-fitting
+ steps, the top edge of the contour under the tilde is usually aligned
+ with a horizontal grid line. The vertical gap enforced by the vertical
+ adjustment is exactly one pixel, so if the top edge of the contour below
+ the tilde is on a grid line, the resulting tilde contour will also be
+ grid-aligned.
+
+ But in cases where the gap is already big enough so that the vertical
+ adjustment does nothing, this function ensures that even without the
+ intervention of the vertical adjustment step, the tilde will be
+ grid-aligned.
+ */
+ static void
+ af_latin_align_tildes( AF_GlyphHints hints )
+ {
+ FT_Int highest_contour = af_find_highest_contour( hints );
+
+ AF_Point p = hints->contours[highest_contour];
+ AF_Point first_point = p;
+
+ FT_Pos min_y, max_y;
+ FT_Pos min_y_rounded;
+ FT_Pos delta;
+
+
+ do
+ {
+ p = p->next;
+ if ( p->y < min_y )
+ min_y = p->y;
+ if ( p->y > max_y )
+ max_y = p->y;
+
+ } while ( p != first_point );
+
+ // mid_y = ( min_y + max_y ) / 2;
+ min_y_rounded = FT_PIX_ROUND( min_y );
+ delta = min_y_rounded - min_y;
+
+ do
+ {
+ p = p->next;
+ p->y += delta;
+
+ } while ( p != 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`. */
@@ -2934,6 +3228,9 @@
adjustment_amount = 64 - ( highest_min_y - max_y );
}
+ FT_TRACE4(( " Calculated adjustment amount: %d\n",
+ adjustment_amount ));
+
if ( adjustment_amount > 64 )
FT_TRACE4(( " Calculated adjustment amount %d"
" was more than threshold of 64. Not adjusting\n",
@@ -3860,6 +4157,47 @@
}
+#ifdef FT_DEBUG_LEVEL_TRACE
+ /* Print the height of the topmost contour for debugging purposes. */
+ /* TODO: remove this once the tilde unflattening works. */
+ static void
+ af_latin_trace_height( FT_UInt num,
+ AF_GlyphHints hints )
+ {
+ AF_Point p = hints->contours[af_find_highest_contour(hints)];
+ AF_Point first_point = p;
+
+ FT_Pos min_y, max_y;
+
+
+ min_y = max_y = p->y;
+
+ do
+ {
+ p = p->next;
+ if ( !(p->flags & AF_FLAG_CONTROL) )
+ {
+ if ( p->y < min_y )
+ min_y = p->y;
+ if ( p->y > max_y )
+ max_y = p->y;
+ }
+
+ } while ( p != first_point );
+
+ FT_TRACE4(( "height %d: %ld\n", num, max_y - min_y ));
+ }
+#else
+ static void
+ af_latin_trace_height( FT_UInt num,
+ AF_GlyphHints hints )
+ {
+ FT_UNUSED( num );
+ FT_UNUSED( hints );
+ }
+#endif
+
+
/* Apply the complete hinting algorithm to a latin glyph. */
static FT_Error
@@ -3894,11 +4232,26 @@
if ( AF_HINTS_DO_VERTICAL( hints ) )
{
+ FT_Bool is_tilde = af_lookup_tilde_correction_type(
+ metrics->root.reverse_charmap, glyph_index );
+
+
+ if ( is_tilde )
+ {
+ af_latin_trace_height(0, hints );
+ af_latin_stretch_tildes( hints );
+ af_latin_align_tildes( hints );
+ af_latin_trace_height(1, hints );
+ }
+
axis = &metrics->axis[AF_DIMENSION_VERT];
error = af_latin_hints_detect_features( hints,
axis->width_count,
axis->widths,
AF_DIMENSION_VERT );
+ if ( is_tilde )
+ af_latin_remove_tilde_points_from_edges( hints );
+
if ( error )
goto Exit;
@@ -3914,14 +4267,19 @@
( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
{
af_latin_hint_edges( hints, (AF_Dimension)dim );
+ af_latin_trace_height(2, hints );
af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
+ af_latin_trace_height(3, hints );
af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
+ af_latin_trace_height(4, hints );
af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
+ af_latin_trace_height(5, hints );
af_glyph_hints_apply_vertical_separation_adjustments(
hints,
(AF_Dimension)dim,
glyph_index,
metrics->root.reverse_charmap );
+ af_latin_trace_height(6, hints );
}
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [freetype2] adjust adf5129ad 5/7: [autofit] Add tilde-unflattening algorithm.,
Werner Lemberg <=