📄 afcjk.c
字号:
/***************************************************************************/
/* */
/* afcjk.c */
/* */
/* Auto-fitter hinting routines for CJK script (body). */
/* */
/* Copyright 2006 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and 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. */
/* */
/***************************************************************************/
/*
* The algorithm is based on akito's autohint patch, available here:
*
* http://www.kde.gr.jp/~akito/patch/freetype2/
*
*/
#include "aftypes.h"
#include "aflatin.h"
#ifdef AF_CONFIG_OPTION_CJK
#include "afcjk.h"
#include "aferrors.h"
#ifdef AF_USE_WARPER
#include "afwarp.h"
#endif
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** C J K G L O B A L M E T R I C S *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
static FT_Error
af_cjk_metrics_init( AF_LatinMetrics metrics,
FT_Face face )
{
FT_CharMap oldmap = face->charmap;
metrics->units_per_em = face->units_per_EM;
/* TODO are there blues? */
if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
face->charmap = NULL;
/* latin's version would suffice */
af_latin_metrics_init_widths( metrics, face, 0x7530 );
FT_Set_Charmap( face, oldmap );
return AF_Err_Ok;
}
static void
af_cjk_metrics_scale_dim( AF_LatinMetrics metrics,
AF_Scaler scaler,
AF_Dimension dim )
{
AF_LatinAxis axis;
axis = &metrics->axis[dim];
if ( dim == AF_DIMENSION_HORZ )
{
axis->scale = scaler->x_scale;
axis->delta = scaler->x_delta;
}
else
{
axis->scale = scaler->y_scale;
axis->delta = scaler->y_delta;
}
}
static void
af_cjk_metrics_scale( AF_LatinMetrics metrics,
AF_Scaler scaler )
{
metrics->root.scaler = *scaler;
af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** C J K G L Y P H A N A L Y S I S *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
static FT_Error
af_cjk_hints_compute_segments( AF_GlyphHints hints,
AF_Dimension dim )
{
AF_AxisHints axis = &hints->axis[dim];
AF_Segment segments = axis->segments;
AF_Segment segment_limit = segments + axis->num_segments;
FT_Error error;
AF_Segment seg;
error = af_latin_hints_compute_segments( hints, dim );
if ( error )
return error;
/* a segment is round if it doesn't have successive */
/* on-curve points. */
for ( seg = segments; seg < segment_limit; seg++ )
{
AF_Point pt = seg->first;
AF_Point last = seg->last;
AF_Flags f0 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
AF_Flags f1;
seg->flags &= ~AF_EDGE_ROUND;
for ( ; pt != last; f0 = f1 )
{
pt = pt->next;
f1 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
if ( !f0 && !f1 )
break;
if ( pt == last )
seg->flags |= AF_EDGE_ROUND;
}
}
return AF_Err_Ok;
}
static void
af_cjk_hints_link_segments( AF_GlyphHints hints,
AF_Dimension dim )
{
AF_AxisHints axis = &hints->axis[dim];
AF_Segment segments = axis->segments;
AF_Segment segment_limit = segments + axis->num_segments;
AF_Direction major_dir = axis->major_dir;
AF_Segment seg1, seg2;
FT_Pos len_threshold;
FT_Pos dist_threshold;
len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
: hints->y_scale;
dist_threshold = FT_DivFix( 64 * 3, dist_threshold );
/* now compare each segment to the others */
for ( seg1 = segments; seg1 < segment_limit; seg1++ )
{
/* the fake segments are for metrics hinting only */
if ( seg1->first == seg1->last )
continue;
if ( seg1->dir != major_dir )
continue;
for ( seg2 = segments; seg2 < segment_limit; seg2++ )
if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 )
{
FT_Pos dist = seg2->pos - seg1->pos;
if ( dist < 0 )
continue;
{
FT_Pos min = seg1->min_coord;
FT_Pos max = seg1->max_coord;
FT_Pos len;
if ( min < seg2->min_coord )
min = seg2->min_coord;
if ( max > seg2->max_coord )
max = seg2->max_coord;
len = max - min;
if ( len >= len_threshold )
{
if ( dist * 8 < seg1->score * 9 &&
( dist * 8 < seg1->score * 7 || seg1->len < len ) )
{
seg1->score = dist;
seg1->len = len;
seg1->link = seg2;
}
if ( dist * 8 < seg2->score * 9 &&
( dist * 8 < seg2->score * 7 || seg2->len < len ) )
{
seg2->score = dist;
seg2->len = len;
seg2->link = seg1;
}
}
}
}
}
/*
* now compute the `serif' segments
*
* In Hanzi, some strokes are wider on one or both of the ends.
* We either identify the stems on the ends as serifs or remove
* the linkage, depending on the length of the stems.
*
*/
{
AF_Segment link1, link2;
for ( seg1 = segments; seg1 < segment_limit; seg1++ )
{
link1 = seg1->link;
if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos )
continue;
if ( seg1->score >= dist_threshold )
continue;
for ( seg2 = segments; seg2 < segment_limit; seg2++ )
{
if ( seg2->pos > seg1->pos || seg1 == seg2 )
continue;
link2 = seg2->link;
if ( !link2 || link2->link != seg2 || link2->pos < link1->pos )
continue;
if ( seg1->pos == seg2->pos && link1->pos == link2->pos )
continue;
if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score )
continue;
/* seg2 < seg1 < link1 < link2 */
if ( seg1->len >= seg2->len * 3 )
{
AF_Segment seg;
for ( seg = segments; seg < segment_limit; seg++ )
{
AF_Segment link = seg->link;
if ( link == seg2 )
{
seg->link = 0;
seg->serif = link1;
}
else if ( link == link2 )
{
seg->link = 0;
seg->serif = seg1;
}
}
}
else
{
seg1->link = link1->link = 0;
break;
}
}
}
}
for ( seg1 = segments; seg1 < segment_limit; seg1++ )
{
seg2 = seg1->link;
if ( seg2 )
{
seg2->num_linked++;
if ( seg2->link != seg1 )
{
seg1->link = 0;
if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 )
seg1->serif = seg2->link;
else
seg2->num_linked--;
}
}
}
}
static FT_Error
af_cjk_hints_compute_edges( AF_GlyphHints hints,
AF_Dimension dim )
{
AF_AxisHints axis = &hints->axis[dim];
FT_Error error = AF_Err_Ok;
FT_Memory memory = hints->memory;
AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
AF_Segment segments = axis->segments;
AF_Segment segment_limit = segments + axis->num_segments;
AF_Segment seg;
AF_Direction up_dir;
FT_Fixed scale;
FT_Pos edge_distance_threshold;
axis->num_edges = 0;
scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
: hints->y_scale;
up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
: AF_DIR_RIGHT;
/*********************************************************************/
/* */
/* We begin by generating a sorted table of edges for the current */
/* direction. To do so, we simply scan each segment and try to find */
/* an edge in our table that corresponds to its position. */
/* */
/* If no edge is found, we create and insert a new edge in the */
/* sorted table. Otherwise, we simply add the segment to the edge's */
/* list which is then processed in the second step to compute the */
/* edge's properties. */
/* */
/* Note that the edges table is sorted along the segment/edge */
/* position. */
/* */
/*********************************************************************/
edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
scale );
if ( edge_distance_threshold > 64 / 4 )
edge_distance_threshold = FT_DivFix( 64 / 4, scale );
else
edge_distance_threshold = laxis->edge_distance_threshold;
for ( seg = segments; seg < segment_limit; seg++ )
{
AF_Edge found = 0;
FT_Pos best = 0xFFFFU;
FT_Int ee;
/* look for an edge corresponding to the segment */
for ( ee = 0; ee < axis->num_edges; ee++ )
{
AF_Edge edge = axis->edges + ee;
FT_Pos dist;
if ( edge->dir != seg->dir )
continue;
dist = seg->pos - edge->fpos;
if ( dist < 0 )
dist = -dist;
if ( dist < edge_distance_threshold && dist < best )
{
AF_Segment link = seg->link;
/* check whether all linked segments of the candidate edge */
/* can make a single edge. */
if ( link )
{
AF_Segment seg1 = edge->first;
AF_Segment link1;
FT_Pos dist2 = 0;
do
{
link1 = seg1->link;
if ( link1 )
{
dist2 = AF_SEGMENT_DIST( link, link1 );
if ( dist2 >= edge_distance_threshold )
break;
}
} while ( ( seg1 = seg1->edge_next ) != edge->first );
if ( dist2 >= edge_distance_threshold )
continue;
}
best = dist;
found = edge;
}
}
if ( !found )
{
AF_Edge edge;
/* insert a new edge in the list and */
/* sort according to the position */
error = af_axis_hints_new_edge( axis, seg->pos, memory, &edge );
if ( error )
goto Exit;
/* add the segment to the new edge's list */
FT_ZERO( edge );
edge->first = seg;
edge->last = seg;
edge->fpos = seg->pos;
edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
seg->edge_next = seg;
edge->dir = seg->dir;
}
else
{
/* if an edge was found, simply add the segment to the edge's */
/* list */
seg->edge_next = found->first;
found->last->edge_next = seg;
found->last = seg;
}
}
/*********************************************************************/
/* */
/* Good, we now compute each edge's properties according to segments */
/* found on its position. Basically, these are as follows. */
/* */
/* - edge's main direction */
/* - stem edge, serif edge or both (which defaults to stem then) */
/* - rounded edge, straight or both (which defaults to straight) */
/* - link for edge */
/* */
/*********************************************************************/
/* first of all, set the `edge' field in each segment -- this is */
/* required in order to compute edge links */
/* */
/* Note that removing this loop and setting the `edge' field of each */
/* segment directly in the code above slows down execution speed for */
/* some reasons on platforms like the Sun. */
{
AF_Edge edges = axis->edges;
AF_Edge edge_limit = edges + axis->num_edges;
AF_Edge edge;
for ( edge = edges; edge < edge_limit; edge++ )
{
seg = edge->first;
if ( seg )
do
{
seg->edge = edge;
seg = seg->edge_next;
} while ( seg != edge->first );
}
/* now compute each edge properties */
for ( edge = edges; edge < edge_limit; edge++ )
{
FT_Int is_round = 0; /* does it contain round segments? */
FT_Int is_straight = 0; /* does it contain straight segments? */
seg = edge->first;
do
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -