1294 lines
38 KiB
C
1294 lines
38 KiB
C
|
/***************************************************************************/
|
||
|
/* */
|
||
|
/* ahhint.c */
|
||
|
/* */
|
||
|
/* Glyph hinter */
|
||
|
/* */
|
||
|
/* Copyright 2000: Catharon Productions Inc. */
|
||
|
/* Author: David Turner */
|
||
|
/* */
|
||
|
/* This file is part of the Catharon Typography Project and shall only */
|
||
|
/* be used, modified, and distributed under the terms of the Catharon */
|
||
|
/* Open Source License that should come with this file under the name */
|
||
|
/* "CatharonLicense.txt". By continuing to use, modify, or distribute */
|
||
|
/* this file you indicate that you have read the license and */
|
||
|
/* understand and accept it fully. */
|
||
|
/* */
|
||
|
/* Note that this license is compatible with the FreeType license */
|
||
|
/* */
|
||
|
/***************************************************************************/
|
||
|
|
||
|
#ifdef FT_FLAT_COMPILE
|
||
|
#include "ahhint.h"
|
||
|
#include "ahglyph.h"
|
||
|
#include "ahangles.h"
|
||
|
#else
|
||
|
#include <autohint/ahhint.h>
|
||
|
#include <autohint/ahglyph.h>
|
||
|
#include <autohint/ahangles.h>
|
||
|
#endif
|
||
|
|
||
|
#include <freetype/ftoutln.h>
|
||
|
|
||
|
#define FACE_GLOBALS(face) ((AH_Face_Globals*)(face)->autohint.data)
|
||
|
|
||
|
#define AH_USE_IUP
|
||
|
|
||
|
|
||
|
/*******************************************************************/
|
||
|
/*******************************************************************/
|
||
|
/**** ****/
|
||
|
/**** Hinting routines ****/
|
||
|
/**** ****/
|
||
|
/*******************************************************************/
|
||
|
/*******************************************************************/
|
||
|
|
||
|
static int disable_horz_edges = 0;
|
||
|
static int disable_vert_edges = 0;
|
||
|
|
||
|
/* snap a given width in scaled coordinates to one of the */
|
||
|
/* current standard widths.. */
|
||
|
static
|
||
|
FT_Pos ah_snap_width( FT_Pos* widths,
|
||
|
FT_Int count,
|
||
|
FT_Pos width )
|
||
|
{
|
||
|
int n;
|
||
|
FT_Pos best = 64+32+2;
|
||
|
FT_Pos reference = width;
|
||
|
|
||
|
for ( n = 0; n < count; n++ )
|
||
|
{
|
||
|
FT_Pos w;
|
||
|
FT_Pos dist;
|
||
|
|
||
|
w = widths[n];
|
||
|
dist = width - w;
|
||
|
if (dist < 0) dist = -dist;
|
||
|
if (dist < best)
|
||
|
{
|
||
|
best = dist;
|
||
|
reference = w;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( width >= reference )
|
||
|
{
|
||
|
width -= 0x21;
|
||
|
if ( width < reference )
|
||
|
width = reference;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
width += 0x21;
|
||
|
if ( width > reference )
|
||
|
width = reference;
|
||
|
}
|
||
|
|
||
|
return width;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Align one stem edge relative to the previous stem edge */
|
||
|
static
|
||
|
void ah_align_linked_edge( AH_Hinter* hinter,
|
||
|
AH_Edge* base_edge,
|
||
|
AH_Edge* stem_edge,
|
||
|
int vertical )
|
||
|
{
|
||
|
FT_Pos dist = stem_edge->opos - base_edge->opos;
|
||
|
AH_Globals* globals = &hinter->globals->scaled;
|
||
|
FT_Pos sign = 1;
|
||
|
|
||
|
if (dist < 0)
|
||
|
{
|
||
|
dist = -dist;
|
||
|
sign = -1;
|
||
|
}
|
||
|
|
||
|
if (vertical)
|
||
|
{
|
||
|
dist = ah_snap_width( globals->heights, globals->num_heights, dist );
|
||
|
|
||
|
/* in the case of vertical hinting, always round */
|
||
|
/* the stem heights to integer pixels.. */
|
||
|
if (dist >= 64)
|
||
|
dist = (dist+16) & -64;
|
||
|
else
|
||
|
dist = 64;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dist = ah_snap_width( globals->widths, globals->num_widths, dist );
|
||
|
|
||
|
if (hinter->flags & ah_hinter_monochrome)
|
||
|
{
|
||
|
/* monochrome horizontal hinting: snap widths to integer pixels */
|
||
|
/* with a different threshold.. */
|
||
|
if (dist < 64)
|
||
|
dist = 64;
|
||
|
else
|
||
|
dist = (dist+32) & -64;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* for horizontal anti-aliased hinting, we adopt a more subtle */
|
||
|
/* approach, we strengthen small stems, round stems whose size */
|
||
|
/* is between 1 and 2 pixels to an integer, otherwise nothing */
|
||
|
if (dist < 48)
|
||
|
dist = (dist+64) >> 1;
|
||
|
|
||
|
else if (dist < 128)
|
||
|
dist = (dist+42) & -64;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
stem_edge->pos = base_edge->pos + sign*dist;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static
|
||
|
void ah_align_serif_edge( AH_Hinter* hinter,
|
||
|
AH_Edge* base,
|
||
|
AH_Edge* serif )
|
||
|
{
|
||
|
FT_Pos dist;
|
||
|
FT_Pos sign = 1;
|
||
|
|
||
|
UNUSED(hinter);
|
||
|
dist = serif->opos - base->opos;
|
||
|
if (dist < 0)
|
||
|
{
|
||
|
dist = -dist;
|
||
|
sign = -1;
|
||
|
}
|
||
|
|
||
|
if (base->flags & ah_edge_done)
|
||
|
/* do not strengthen serifs */
|
||
|
{
|
||
|
if (dist > 64)
|
||
|
dist = (dist+16) & -64;
|
||
|
|
||
|
else if (dist <= 32)
|
||
|
dist = (dist+33) >> 1;
|
||
|
}
|
||
|
serif->pos = base->pos + sign*dist;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**** ****/
|
||
|
/**** E D G E H I N T I N G ****/
|
||
|
/**** ****/
|
||
|
/**** ****/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
|
||
|
/* Another alternative edge hinting algorithm */
|
||
|
static
|
||
|
void ah_hint_edges_3( AH_Hinter* hinter )
|
||
|
{
|
||
|
AH_Edge* edges;
|
||
|
AH_Edge* edge_limit;
|
||
|
AH_Outline* outline = hinter->glyph;
|
||
|
FT_Int dimension;
|
||
|
|
||
|
edges = outline->horz_edges;
|
||
|
edge_limit = edges + outline->num_hedges;
|
||
|
|
||
|
for ( dimension = 1; dimension >= 0; dimension-- )
|
||
|
{
|
||
|
AH_Edge* edge;
|
||
|
AH_Edge* before = 0;
|
||
|
AH_Edge* after = 0;
|
||
|
AH_Edge* anchor = 0;
|
||
|
int has_serifs = 0;
|
||
|
|
||
|
if (disable_vert_edges && !dimension)
|
||
|
goto Next_Dimension;
|
||
|
|
||
|
if (disable_horz_edges && dimension)
|
||
|
goto Next_Dimension;
|
||
|
|
||
|
/* we begin by aligning all stems relative to the blue zone */
|
||
|
/* if needed.. that's only for horizontal edges.. */
|
||
|
if (dimension)
|
||
|
{
|
||
|
for ( edge = edges; edge < edge_limit; edge++ )
|
||
|
{
|
||
|
FT_Pos* blue;
|
||
|
AH_Edge *edge1, *edge2;
|
||
|
|
||
|
if (edge->flags & ah_edge_done)
|
||
|
continue;
|
||
|
|
||
|
blue = edge->blue_edge;
|
||
|
edge1 = 0;
|
||
|
edge2 = edge->link;
|
||
|
|
||
|
if (blue)
|
||
|
{
|
||
|
edge1 = edge;
|
||
|
}
|
||
|
else if (edge2 && edge2->blue_edge)
|
||
|
{
|
||
|
blue = edge2->blue_edge;
|
||
|
edge1 = edge2;
|
||
|
edge2 = edge;
|
||
|
}
|
||
|
|
||
|
if (!edge1)
|
||
|
continue;
|
||
|
|
||
|
edge1->pos = blue[0];
|
||
|
edge1->flags |= ah_edge_done;
|
||
|
|
||
|
if (edge2 && !edge2->blue_edge)
|
||
|
{
|
||
|
ah_align_linked_edge( hinter, edge1, edge2, dimension );
|
||
|
edge2->flags |= ah_edge_done;
|
||
|
}
|
||
|
|
||
|
if (!anchor)
|
||
|
anchor = edge;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* now, we will align all stem edges, trying to maintain the */
|
||
|
/* relative order of stems in the glyph.. */
|
||
|
before = 0;
|
||
|
after = 0;
|
||
|
for ( edge = edges; edge < edge_limit; edge++ )
|
||
|
{
|
||
|
AH_Edge *edge2;
|
||
|
|
||
|
if (edge->flags & ah_edge_done)
|
||
|
continue;
|
||
|
|
||
|
/* skip all non-stem edges */
|
||
|
edge2 = edge->link;
|
||
|
if (!edge2)
|
||
|
{
|
||
|
has_serifs++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* now, align the stem */
|
||
|
|
||
|
/* this should not happen, but it's better to be safe.. */
|
||
|
if (edge2->blue_edge || edge2 < edge)
|
||
|
{
|
||
|
#if 0
|
||
|
printf( "strange blue alignement, edge %d to %d\n",
|
||
|
edge - edges, edge2 - edges );
|
||
|
#endif
|
||
|
ah_align_linked_edge( hinter, edge2, edge, dimension );
|
||
|
edge->flags |= ah_edge_done;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
FT_Bool min = 0;
|
||
|
FT_Pos delta;
|
||
|
|
||
|
if (!anchor)
|
||
|
{
|
||
|
edge->pos = (edge->opos+32) & -64;
|
||
|
anchor = edge;
|
||
|
}
|
||
|
else
|
||
|
edge->pos = anchor->pos + ((edge->opos - anchor->opos + 32) & -64);
|
||
|
|
||
|
edge->flags |= ah_edge_done;
|
||
|
|
||
|
if (edge > edges && edge->pos < edge[-1].pos)
|
||
|
{
|
||
|
edge->pos = edge[-1].pos;
|
||
|
min = 1;
|
||
|
}
|
||
|
|
||
|
ah_align_linked_edge( hinter, edge, edge2, dimension );
|
||
|
delta = 0;
|
||
|
if ( edge2+1 < edge_limit &&
|
||
|
edge2[1].flags & ah_edge_done )
|
||
|
delta = edge2[1].pos - edge2->pos;
|
||
|
|
||
|
if (delta < 0)
|
||
|
{
|
||
|
edge2->pos += delta;
|
||
|
if (!min)
|
||
|
edge->pos += delta;
|
||
|
}
|
||
|
edge2->flags |= ah_edge_done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!has_serifs)
|
||
|
goto Next_Dimension;
|
||
|
|
||
|
/* now, hint the remaining edges (serifs and single) in order */
|
||
|
/* to complete our processing.. */
|
||
|
for ( edge = edges; edge < edge_limit; edge++ )
|
||
|
{
|
||
|
if (edge->flags & ah_edge_done)
|
||
|
continue;
|
||
|
|
||
|
if (edge->serif)
|
||
|
{
|
||
|
ah_align_serif_edge( hinter, edge->serif, edge );
|
||
|
}
|
||
|
else if (!anchor)
|
||
|
{
|
||
|
edge->pos = (edge->opos+32) & -64;
|
||
|
anchor = edge;
|
||
|
}
|
||
|
else
|
||
|
edge->pos = anchor->pos + ((edge->opos-anchor->opos+32) & -64);
|
||
|
|
||
|
edge->flags |= ah_edge_done;
|
||
|
|
||
|
if (edge > edges && edge->pos < edge[-1].pos)
|
||
|
edge->pos = edge[-1].pos;
|
||
|
|
||
|
if ( edge+1 < edge_limit && edge[1].flags & ah_edge_done &&
|
||
|
edge->pos > edge[1].pos)
|
||
|
edge->pos = edge[1].pos;
|
||
|
}
|
||
|
|
||
|
Next_Dimension:
|
||
|
edges = outline->vert_edges;
|
||
|
edge_limit = edges + outline->num_vedges;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
LOCAL_FUNC
|
||
|
void ah_hinter_hint_edges( AH_Hinter* hinter,
|
||
|
int no_horz_edges,
|
||
|
int no_vert_edges )
|
||
|
{
|
||
|
disable_horz_edges = no_horz_edges;
|
||
|
disable_vert_edges = no_vert_edges;
|
||
|
|
||
|
/* AH_Interpolate_Blue_Edges( hinter ); -- doesn't seem to help */
|
||
|
/* reduce the problem of the disappearing eye in the "e" of Times */
|
||
|
/* also, creates some artifacts near the blue zones ?? */
|
||
|
{
|
||
|
ah_hint_edges_3( hinter );
|
||
|
|
||
|
/* outline optimiser removed temporarily */
|
||
|
#if 0
|
||
|
if (hinter->flags & ah_hinter_optimize)
|
||
|
{
|
||
|
AH_Optimizer opt;
|
||
|
|
||
|
if (!AH_Optimizer_Init( &opt, hinter->glyph, hinter->memory ))
|
||
|
{
|
||
|
AH_Optimizer_Compute( &opt );
|
||
|
AH_Optimizer_Done( &opt );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**** ****/
|
||
|
/**** P O I N T H I N T I N G ****/
|
||
|
/**** ****/
|
||
|
/**** ****/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
|
||
|
static
|
||
|
void ah_hinter_align_edge_points( AH_Hinter* hinter )
|
||
|
{
|
||
|
AH_Outline* outline = hinter->glyph;
|
||
|
AH_Edge* edges;
|
||
|
AH_Edge* edge_limit;
|
||
|
FT_Int dimension;
|
||
|
|
||
|
edges = outline->horz_edges;
|
||
|
edge_limit = edges + outline->num_hedges;
|
||
|
|
||
|
for ( dimension = 1; dimension >= 0; dimension-- )
|
||
|
{
|
||
|
AH_Edge* edge;
|
||
|
AH_Edge* before;
|
||
|
AH_Edge* after;
|
||
|
|
||
|
before = 0;
|
||
|
after = 0;
|
||
|
|
||
|
edge = edges;
|
||
|
for ( ; edge < edge_limit; edge++ )
|
||
|
{
|
||
|
/* move the points of each segment in each edge to the edge's position */
|
||
|
AH_Segment* seg = edge->first;
|
||
|
do
|
||
|
{
|
||
|
AH_Point* point = seg->first;
|
||
|
for (;;)
|
||
|
{
|
||
|
if (dimension)
|
||
|
{
|
||
|
point->y = edge->pos;
|
||
|
point->flags |= ah_flah_touch_y;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
point->x = edge->pos;
|
||
|
point->flags |= ah_flah_touch_x;
|
||
|
}
|
||
|
if (point == seg->last)
|
||
|
break;
|
||
|
|
||
|
point = point->next;
|
||
|
}
|
||
|
|
||
|
seg = seg->edge_next;
|
||
|
}
|
||
|
while (seg != edge->first);
|
||
|
}
|
||
|
edges = outline->vert_edges;
|
||
|
edge_limit = edges + outline->num_vedges;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* hint the strong points - this is equivalent to the TrueType "IP" */
|
||
|
static
|
||
|
void ah_hinter_align_strong_points( AH_Hinter* hinter )
|
||
|
{
|
||
|
AH_Outline* outline = hinter->glyph;
|
||
|
FT_Int dimension;
|
||
|
AH_Edge* edges;
|
||
|
AH_Edge* edge_limit;
|
||
|
AH_Point* points;
|
||
|
AH_Point* point_limit;
|
||
|
AH_Flags touch_flag;
|
||
|
|
||
|
points = outline->points;
|
||
|
point_limit = points + outline->num_points;
|
||
|
|
||
|
edges = outline->horz_edges;
|
||
|
edge_limit = edges + outline->num_hedges;
|
||
|
touch_flag = ah_flah_touch_y;
|
||
|
|
||
|
for ( dimension = 1; dimension >= 0; dimension-- )
|
||
|
{
|
||
|
AH_Point* point;
|
||
|
AH_Edge* edge;
|
||
|
AH_Edge* before;
|
||
|
AH_Edge* after;
|
||
|
|
||
|
before = 0;
|
||
|
after = 0;
|
||
|
|
||
|
if ( edges < edge_limit )
|
||
|
for ( point = points; point < point_limit; point++ )
|
||
|
{
|
||
|
FT_Pos u, ou, fu; /* point position */
|
||
|
FT_Pos delta;
|
||
|
|
||
|
if ( point->flags & touch_flag )
|
||
|
continue;
|
||
|
|
||
|
#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
|
||
|
/* if this point is candidate to weak interpolation, we'll */
|
||
|
/* interpolate it after all strong points have been processed */
|
||
|
if ( point->flags & ah_flah_weak_interpolation )
|
||
|
continue;
|
||
|
#endif
|
||
|
|
||
|
if (dimension) { u = point->fy; ou = point->oy; }
|
||
|
else { u = point->fx; ou = point->ox; }
|
||
|
|
||
|
fu = u;
|
||
|
|
||
|
/* is the point before the first edge ? */
|
||
|
edge = edges;
|
||
|
delta = edge->fpos - u;
|
||
|
if (delta >= 0)
|
||
|
{
|
||
|
u = edge->pos - (edge->opos - ou);
|
||
|
goto Store_Point;
|
||
|
}
|
||
|
|
||
|
/* is the point after the last edge ? */
|
||
|
edge = edge_limit-1;
|
||
|
delta = u - edge->fpos;
|
||
|
if ( delta >= 0 )
|
||
|
{
|
||
|
u = edge->pos + (ou - edge->opos);
|
||
|
goto Store_Point;
|
||
|
}
|
||
|
|
||
|
/* otherwise, interpolate the point in between */
|
||
|
{
|
||
|
AH_Edge* before = 0;
|
||
|
AH_Edge* after = 0;
|
||
|
|
||
|
for ( edge = edges; edge < edge_limit; edge++ )
|
||
|
{
|
||
|
if ( u == edge->fpos )
|
||
|
{
|
||
|
u = edge->pos;
|
||
|
goto Store_Point;
|
||
|
}
|
||
|
if ( u < edge->fpos )
|
||
|
break;
|
||
|
before = edge;
|
||
|
}
|
||
|
|
||
|
for ( edge = edge_limit-1; edge >= edges; edge-- )
|
||
|
{
|
||
|
if ( u == edge->fpos )
|
||
|
{
|
||
|
u = edge->pos;
|
||
|
goto Store_Point;
|
||
|
}
|
||
|
if ( u > edge->fpos )
|
||
|
break;
|
||
|
after = edge;
|
||
|
}
|
||
|
|
||
|
/* assert( before && after && before != after ) */
|
||
|
u = before->pos + FT_MulDiv( fu - before->fpos,
|
||
|
after->pos - before->pos,
|
||
|
after->fpos - before->fpos );
|
||
|
}
|
||
|
Store_Point:
|
||
|
|
||
|
/* save the point position */
|
||
|
if (dimension) point->y = u;
|
||
|
else point->x = u;
|
||
|
|
||
|
point->flags |= touch_flag;
|
||
|
}
|
||
|
|
||
|
edges = outline->vert_edges;
|
||
|
edge_limit = edges + outline->num_vedges;
|
||
|
touch_flag = ah_flah_touch_x;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
|
||
|
static
|
||
|
void ah_iup_shift( AH_Point* p1,
|
||
|
AH_Point* p2,
|
||
|
AH_Point* ref )
|
||
|
{
|
||
|
AH_Point* p;
|
||
|
FT_Pos delta = ref->u - ref->v;
|
||
|
|
||
|
for ( p = p1; p < ref; p++ )
|
||
|
p->u = p->v + delta;
|
||
|
|
||
|
for ( p = ref+1; p <= p2; p++ )
|
||
|
p->u = p->v + delta;
|
||
|
}
|
||
|
|
||
|
|
||
|
static
|
||
|
void ah_iup_interp( AH_Point* p1,
|
||
|
AH_Point* p2,
|
||
|
AH_Point* ref1,
|
||
|
AH_Point* ref2 )
|
||
|
{
|
||
|
AH_Point* p;
|
||
|
FT_Pos u;
|
||
|
FT_Pos v1 = ref1->v;
|
||
|
FT_Pos v2 = ref2->v;
|
||
|
FT_Pos d1 = ref1->u - v1;
|
||
|
FT_Pos d2 = ref2->u - v2;
|
||
|
|
||
|
if (p1 > p2) return;
|
||
|
|
||
|
if (v1 == v2)
|
||
|
{
|
||
|
for ( p = p1; p <= p2; p++ )
|
||
|
{
|
||
|
FT_Pos u = p->v;
|
||
|
|
||
|
if (u <= v1) u += d1;
|
||
|
else u += d2;
|
||
|
p->u = u;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( v1 < v2 )
|
||
|
{
|
||
|
for ( p = p1; p <= p2; p++ )
|
||
|
{
|
||
|
u = p->v;
|
||
|
|
||
|
if (u <= v1) u += d1;
|
||
|
else if (u >= v2) u += d2;
|
||
|
else u = ref1->u+FT_MulDiv( u-v1, ref2->u-ref1->u, v2-v1 );
|
||
|
|
||
|
p->u = u;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for ( p = p1; p <= p2; p++ )
|
||
|
{
|
||
|
u = p->v;
|
||
|
if (u <= v2) u += d2;
|
||
|
else if (u >= v1) u += d1;
|
||
|
else u = ref1->u+FT_MulDiv( u-v1, ref2->u-ref1->u, v2-v1 );
|
||
|
|
||
|
p->u = u;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* interpolate weak points - this is equivalent to the TrueType "IUP" */
|
||
|
static
|
||
|
void ah_hinter_align_weak_points( AH_Hinter* hinter )
|
||
|
{
|
||
|
AH_Outline* outline = hinter->glyph;
|
||
|
FT_Int dimension;
|
||
|
AH_Edge* edges;
|
||
|
AH_Edge* edge_limit;
|
||
|
AH_Point* points;
|
||
|
AH_Point* point_limit;
|
||
|
AH_Point** contour_limit;
|
||
|
AH_Flags touch_flag;
|
||
|
|
||
|
points = outline->points;
|
||
|
point_limit = points + outline->num_points;
|
||
|
|
||
|
/* PASS 1 : Move segment points to edge positions */
|
||
|
|
||
|
edges = outline->horz_edges;
|
||
|
edge_limit = edges + outline->num_hedges;
|
||
|
touch_flag = ah_flah_touch_y;
|
||
|
|
||
|
contour_limit = outline->contours + outline->num_contours;
|
||
|
|
||
|
ah_setup_uv( outline, ah_uv_oy );
|
||
|
|
||
|
for ( dimension = 1; dimension >= 0; dimension-- )
|
||
|
{
|
||
|
AH_Point* point;
|
||
|
AH_Point* end_point;
|
||
|
AH_Point* first_point;
|
||
|
AH_Point** contour;
|
||
|
|
||
|
point = points;
|
||
|
contour = outline->contours;
|
||
|
|
||
|
for ( ; contour < contour_limit; contour++ )
|
||
|
{
|
||
|
point = *contour;
|
||
|
end_point = point->prev;
|
||
|
first_point = point;
|
||
|
|
||
|
while (point <= end_point && !(point->flags & touch_flag))
|
||
|
point++;
|
||
|
|
||
|
if (point <= end_point)
|
||
|
{
|
||
|
AH_Point* first_touched = point;
|
||
|
AH_Point* cur_touched = point;
|
||
|
|
||
|
point++;
|
||
|
while ( point <= end_point )
|
||
|
{
|
||
|
if (point->flags & touch_flag)
|
||
|
{
|
||
|
/* we found two succesive touched points, we interpolate */
|
||
|
/* all contour points between them.. */
|
||
|
ah_iup_interp( cur_touched+1, point-1,
|
||
|
cur_touched, point );
|
||
|
cur_touched = point;
|
||
|
}
|
||
|
point++;
|
||
|
}
|
||
|
|
||
|
if (cur_touched == first_touched)
|
||
|
{
|
||
|
/* this is a special case: only one point was touched in the */
|
||
|
/* contour.. we thus simply shift the whole contour.. */
|
||
|
ah_iup_shift( first_point, end_point, cur_touched );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* now interpolate after the last touched point to the end */
|
||
|
/* of the contour.. */
|
||
|
ah_iup_interp( cur_touched+1, end_point,
|
||
|
cur_touched, first_touched );
|
||
|
|
||
|
/* if the first contour point isn't touched, interpolate */
|
||
|
/* from the contour start to the first touched point */
|
||
|
if (first_touched > points)
|
||
|
ah_iup_interp( first_point, first_touched-1,
|
||
|
cur_touched, first_touched );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* now save the interpolated values back to x/y */
|
||
|
if (dimension)
|
||
|
{
|
||
|
for ( point = points; point < point_limit; point++ )
|
||
|
point->y = point->u;
|
||
|
|
||
|
touch_flag = ah_flah_touch_x;
|
||
|
ah_setup_uv( outline, ah_uv_ox );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for ( point = points; point < point_limit; point++ )
|
||
|
point->x = point->u;
|
||
|
|
||
|
break; /* exit loop */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
LOCAL_FUNC
|
||
|
void ah_hinter_align_points( AH_Hinter* hinter )
|
||
|
{
|
||
|
ah_hinter_align_edge_points( hinter );
|
||
|
|
||
|
#ifndef AH_OPTION_NO_STRONG_INTERPOLATION
|
||
|
ah_hinter_align_strong_points( hinter );
|
||
|
#endif
|
||
|
|
||
|
#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
|
||
|
ah_hinter_align_weak_points( hinter );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**** ****/
|
||
|
/**** H I N T E R O B J E C T M E T H O D S ****/
|
||
|
/**** ****/
|
||
|
/**** ****/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
/**************************************************************************/
|
||
|
|
||
|
/* scale and fit the global metrics */
|
||
|
static
|
||
|
void ah_hinter_scale_globals( AH_Hinter* hinter,
|
||
|
FT_Fixed x_scale,
|
||
|
FT_Fixed y_scale )
|
||
|
{
|
||
|
FT_Int n;
|
||
|
AH_Face_Globals* globals = hinter->globals;
|
||
|
AH_Globals* design = &globals->design;
|
||
|
AH_Globals* scaled = &globals->scaled;
|
||
|
|
||
|
/* copy content */
|
||
|
*scaled = *design;
|
||
|
|
||
|
/* scale the standard widths & heights */
|
||
|
for ( n = 0; n < design->num_widths; n++ )
|
||
|
scaled->widths[n] = FT_MulFix( design->widths[n], x_scale );
|
||
|
|
||
|
for ( n = 0; n < design->num_heights; n++ )
|
||
|
scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
|
||
|
|
||
|
/* scale the blue zones */
|
||
|
for ( n = 0; n < ah_blue_max; n++ )
|
||
|
{
|
||
|
FT_Pos delta, delta2;
|
||
|
|
||
|
delta = design->blue_shoots[n] - design->blue_refs[n];
|
||
|
delta2 = delta; if (delta < 0) delta2 = -delta2;
|
||
|
delta2 = FT_MulFix( delta2, y_scale );
|
||
|
|
||
|
if (delta2 < 32)
|
||
|
delta2 = 0;
|
||
|
else if (delta2 < 64)
|
||
|
delta2 = 32 + (((delta2-32)+16) & -32);
|
||
|
else
|
||
|
delta2 = (delta2+32) & -64;
|
||
|
|
||
|
if (delta < 0) delta2 = -delta2;
|
||
|
|
||
|
scaled->blue_refs [n] = (FT_MulFix(design->blue_refs[n],y_scale)+32) & -64;
|
||
|
scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static
|
||
|
void ah_hinter_align( AH_Hinter* hinter )
|
||
|
{
|
||
|
ah_hinter_align_edge_points( hinter );
|
||
|
ah_hinter_align_points( hinter );
|
||
|
}
|
||
|
|
||
|
|
||
|
/* finalise a hinter object */
|
||
|
void ah_hinter_done( AH_Hinter* hinter )
|
||
|
{
|
||
|
if (hinter)
|
||
|
{
|
||
|
FT_Memory memory = hinter->memory;
|
||
|
|
||
|
ah_loader_done( hinter->loader );
|
||
|
ah_outline_done( hinter->glyph );
|
||
|
|
||
|
/* note: the globals pointer is _not_ owned by the hinter */
|
||
|
/* but by the current face object, we don't need to */
|
||
|
/* release it.. */
|
||
|
hinter->globals = 0;
|
||
|
hinter->face = 0;
|
||
|
|
||
|
FREE(hinter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* create a new empty hinter object */
|
||
|
FT_Error ah_hinter_new( FT_Library library, AH_Hinter* *ahinter )
|
||
|
{
|
||
|
AH_Hinter* hinter = 0;
|
||
|
FT_Memory memory = library->memory;
|
||
|
FT_Error error;
|
||
|
|
||
|
*ahinter = 0;
|
||
|
|
||
|
/* allocate object */
|
||
|
if (ALLOC( hinter, sizeof(*hinter) )) goto Exit;
|
||
|
|
||
|
hinter->memory = memory;
|
||
|
hinter->flags = 0;
|
||
|
|
||
|
/* allocate outline and loader */
|
||
|
error = ah_outline_new( memory, &hinter->glyph ) ||
|
||
|
ah_loader_new ( memory, &hinter->loader ) ||
|
||
|
ah_loader_create_extra( hinter->loader );
|
||
|
if (error) goto Exit;
|
||
|
|
||
|
*ahinter = hinter;
|
||
|
|
||
|
Exit:
|
||
|
if (error)
|
||
|
ah_hinter_done( hinter );
|
||
|
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* create a face's autohint globals */
|
||
|
FT_Error ah_hinter_new_face_globals( AH_Hinter* hinter,
|
||
|
FT_Face face,
|
||
|
AH_Globals* globals )
|
||
|
{
|
||
|
FT_Error error;
|
||
|
FT_Memory memory = hinter->memory;
|
||
|
AH_Face_Globals* face_globals;
|
||
|
|
||
|
if ( ALLOC( face_globals, sizeof(*face_globals) ) )
|
||
|
goto Exit;
|
||
|
|
||
|
hinter->face = face;
|
||
|
hinter->globals = face_globals;
|
||
|
if (globals)
|
||
|
face_globals->design = *globals;
|
||
|
else
|
||
|
ah_hinter_compute_globals( hinter );
|
||
|
|
||
|
face->autohint.data = face_globals;
|
||
|
face->autohint.finalizer = (FT_Generic_Finalizer)ah_hinter_done_face_globals;
|
||
|
face_globals->face = face;
|
||
|
|
||
|
Exit:
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* discard a face's autohint globals */
|
||
|
void ah_hinter_done_face_globals( AH_Face_Globals* globals )
|
||
|
{
|
||
|
FT_Face face = globals->face;
|
||
|
FT_Memory memory = face->memory;
|
||
|
|
||
|
FREE( globals );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static
|
||
|
FT_Error ah_hinter_load( AH_Hinter* hinter,
|
||
|
FT_UInt glyph_index,
|
||
|
FT_UInt load_flags,
|
||
|
FT_UInt depth )
|
||
|
{
|
||
|
FT_Face face = hinter->face;
|
||
|
FT_GlyphSlot slot = face->glyph;
|
||
|
FT_Fixed x_scale = face->size->metrics.x_scale;
|
||
|
FT_Fixed y_scale = face->size->metrics.y_scale;
|
||
|
FT_Glyph_Metrics metrics; /* temporary metrics */
|
||
|
FT_Error error;
|
||
|
AH_Outline* outline = hinter->glyph;
|
||
|
AH_Loader* gloader = hinter->loader;
|
||
|
FT_Bool no_horz_hints = (load_flags & AH_HINT_NO_HORZ_EDGES) != 0;
|
||
|
FT_Bool no_vert_hints = (load_flags & AH_HINT_NO_VERT_EDGES) != 0;
|
||
|
|
||
|
/* load the glyph */
|
||
|
error = FT_Load_Glyph( face, glyph_index, load_flags );
|
||
|
if (error) goto Exit;
|
||
|
|
||
|
/* save current glyph metrics */
|
||
|
metrics = slot->metrics;
|
||
|
|
||
|
switch (slot->format)
|
||
|
{
|
||
|
case ft_glyph_format_outline:
|
||
|
{
|
||
|
/* first of all, copy the outline points in the loader's current */
|
||
|
/* extra points, which is used to keep original glyph coordinates */
|
||
|
error = ah_loader_check_points( gloader, slot->outline.n_points+2,
|
||
|
slot->outline.n_contours );
|
||
|
if (error) goto Exit;
|
||
|
|
||
|
MEM_Copy( gloader->current.extra_points, slot->outline.points,
|
||
|
slot->outline.n_points * sizeof(FT_Vector) );
|
||
|
|
||
|
MEM_Copy( gloader->current.outline.contours, slot->outline.contours,
|
||
|
slot->outline.n_contours*sizeof(short) );
|
||
|
|
||
|
MEM_Copy( gloader->current.outline.tags, slot->outline.tags,
|
||
|
slot->outline.n_points * sizeof(char) );
|
||
|
|
||
|
gloader->current.outline.n_points = slot->outline.n_points;
|
||
|
gloader->current.outline.n_contours = slot->outline.n_contours;
|
||
|
|
||
|
/* compute original phantom points */
|
||
|
hinter->pp1.x = 0;
|
||
|
hinter->pp1.y = 0;
|
||
|
hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
|
||
|
hinter->pp2.y = 0;
|
||
|
|
||
|
/* be sure to check for spacing glyphs */
|
||
|
if (slot->outline.n_points == 0)
|
||
|
goto Hint_Metrics;
|
||
|
|
||
|
/* now, load the slot image into the auto-outline, and run the */
|
||
|
/* automatic hinting process.. */
|
||
|
error = ah_outline_load( outline, face ); /* XXXX: change to slot */
|
||
|
if (error) goto Exit;
|
||
|
|
||
|
/* perform feature detection */
|
||
|
ah_outline_detect_features( outline );
|
||
|
|
||
|
if ( !no_horz_hints )
|
||
|
{
|
||
|
ah_outline_compute_blue_edges( outline, hinter->globals );
|
||
|
ah_outline_scale_blue_edges( outline, hinter->globals );
|
||
|
}
|
||
|
|
||
|
/* perform alignment control */
|
||
|
ah_hinter_hint_edges( hinter, no_horz_hints, no_vert_hints );
|
||
|
ah_hinter_align( hinter );
|
||
|
|
||
|
/* now save the current outline into the loader's current table */
|
||
|
ah_outline_save( outline, gloader );
|
||
|
|
||
|
/* we now need to hint the metrics according to the change in */
|
||
|
/* width/positioning that occured during the hinting process */
|
||
|
{
|
||
|
FT_Pos old_width, new_width;
|
||
|
FT_Pos old_advance, new_advance;
|
||
|
FT_Pos old_lsb, new_lsb;
|
||
|
AH_Edge* edge1 = hinter->glyph->horz_edges; /* left-most edge */
|
||
|
AH_Edge* edge2 = edge1 + hinter->glyph->num_hedges-1; /* right-mode edge */
|
||
|
|
||
|
old_width = edge2->opos - edge1->opos;
|
||
|
new_width = edge2->pos - edge1->pos;
|
||
|
|
||
|
old_advance = hinter->pp2.x;
|
||
|
old_lsb = edge1->opos;
|
||
|
new_lsb = edge1->pos;
|
||
|
|
||
|
new_advance = old_advance + (new_width+new_lsb-old_width-old_lsb);
|
||
|
|
||
|
hinter->pp1.x = ((new_lsb - old_lsb)+32) & -64;
|
||
|
hinter->pp2.x = ((edge2->pos + (old_advance - edge2->opos))+32) & -64;
|
||
|
}
|
||
|
|
||
|
/* good, we simply add the glyph to our loader's base */
|
||
|
ah_loader_add( gloader );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ft_glyph_format_composite:
|
||
|
{
|
||
|
FT_UInt nn, num_subglyphs = slot->num_subglyphs;
|
||
|
FT_UInt num_base_subgs, start_point, start_contour;
|
||
|
FT_SubGlyph* subglyph;
|
||
|
|
||
|
start_point = gloader->base.outline.n_points;
|
||
|
start_contour = gloader->base.outline.n_contours;
|
||
|
|
||
|
/* first of all, copy the subglyph descriptors in the glyph loader */
|
||
|
error = ah_loader_check_subglyphs( gloader, num_subglyphs );
|
||
|
if (error) goto Exit;
|
||
|
|
||
|
MEM_Copy( gloader->current.subglyphs, slot->subglyphs,
|
||
|
num_subglyphs*sizeof(FT_SubGlyph) );
|
||
|
|
||
|
gloader->current.num_subglyphs = num_subglyphs;
|
||
|
num_base_subgs = gloader->base.num_subglyphs;
|
||
|
|
||
|
/* now, read each subglyph independently */
|
||
|
for ( nn = 0; nn < num_subglyphs; nn++ )
|
||
|
{
|
||
|
FT_Vector pp1, pp2;
|
||
|
FT_Pos x, y;
|
||
|
FT_UInt num_points, num_new_points, num_base_points;
|
||
|
|
||
|
/* gloader.current.subglyphs can change during glyph loading due */
|
||
|
/* to re-allocation. We must recompute the current subglyph on */
|
||
|
/* each iteration.. */
|
||
|
subglyph = gloader->base.subglyphs + num_base_subgs + nn;
|
||
|
|
||
|
pp1 = hinter->pp1;
|
||
|
pp2 = hinter->pp2;
|
||
|
|
||
|
num_base_points = gloader->base.outline.n_points;
|
||
|
|
||
|
error = ah_hinter_load( hinter, subglyph->index, load_flags, depth+1 );
|
||
|
if ( error ) goto Exit;
|
||
|
|
||
|
/* recompute subglyph pointer */
|
||
|
subglyph = gloader->base.subglyphs + num_base_subgs + nn;
|
||
|
|
||
|
if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
|
||
|
{
|
||
|
pp1 = hinter->pp1;
|
||
|
pp2 = hinter->pp2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hinter->pp1 = pp1;
|
||
|
hinter->pp2 = pp2;
|
||
|
}
|
||
|
|
||
|
num_points = gloader->base.outline.n_points;
|
||
|
num_new_points = num_points - num_base_points;
|
||
|
|
||
|
/* now perform the transform required for this subglyph */
|
||
|
|
||
|
if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE |
|
||
|
FT_SUBGLYPH_FLAG_XY_SCALE |
|
||
|
FT_SUBGLYPH_FLAG_2X2 ) )
|
||
|
{
|
||
|
FT_Vector* cur = gloader->base.outline.points + num_base_points;
|
||
|
FT_Vector* org = gloader->base.extra_points + num_base_points;
|
||
|
FT_Vector* limit = cur + num_new_points;
|
||
|
|
||
|
for ( ; cur < limit; cur++, org++ )
|
||
|
{
|
||
|
FT_Vector_Transform( cur, &subglyph->transform );
|
||
|
FT_Vector_Transform( org, &subglyph->transform );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* apply offset */
|
||
|
|
||
|
if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
|
||
|
{
|
||
|
FT_Int k = subglyph->arg1;
|
||
|
FT_UInt l = subglyph->arg2;
|
||
|
FT_Vector* p1;
|
||
|
FT_Vector* p2;
|
||
|
|
||
|
if ( start_point + k >= num_base_points ||
|
||
|
l >= (FT_UInt)num_new_points )
|
||
|
{
|
||
|
error = FT_Err_Invalid_Composite;
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
l += num_base_points;
|
||
|
|
||
|
/* for now, only use the current point coordinates */
|
||
|
/* we may consider another approach in the near future */
|
||
|
p1 = gloader->base.outline.points + start_point + k;
|
||
|
p2 = gloader->base.outline.points + start_point + l;
|
||
|
|
||
|
x = p1->x - p2->x;
|
||
|
y = p1->y - p2->y;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
x = FT_MulFix( subglyph->arg1, x_scale );
|
||
|
y = FT_MulFix( subglyph->arg2, y_scale );
|
||
|
|
||
|
x = ( x + 32 ) & -64;
|
||
|
y = ( y + 32 ) & -64;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
FT_Outline dummy = gloader->base.outline;
|
||
|
dummy.points += num_base_points;
|
||
|
dummy.n_points = num_new_points;
|
||
|
|
||
|
FT_Outline_Translate( &dummy, x, y );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
/* we don't support other formats (yet ?) */
|
||
|
error = FT_Err_Unimplemented_Feature;
|
||
|
}
|
||
|
|
||
|
Hint_Metrics:
|
||
|
if (depth == 0)
|
||
|
{
|
||
|
FT_BBox bbox;
|
||
|
|
||
|
/* we must translate our final outline by -pp1.x, and compute */
|
||
|
/* the new metrics.. */
|
||
|
if (hinter->pp1.x)
|
||
|
FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
|
||
|
|
||
|
FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
|
||
|
bbox.xMin &= -64;
|
||
|
bbox.yMin &= -64;
|
||
|
bbox.xMax = (bbox.xMax+63) & -64;
|
||
|
bbox.yMax = (bbox.yMax+63) & -64;
|
||
|
|
||
|
slot->metrics.width = (bbox.xMax - bbox.xMin);
|
||
|
slot->metrics.height = (bbox.yMax - bbox.yMin);
|
||
|
slot->metrics.horiBearingX = bbox.xMin;
|
||
|
slot->metrics.horiBearingY = bbox.yMax;
|
||
|
slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x;
|
||
|
/* XXXX: TO DO - slot->linearHoriAdvance */
|
||
|
|
||
|
/* now copy outline into glyph slot */
|
||
|
ah_loader_rewind( slot->loader );
|
||
|
error = ah_loader_copy_points( slot->loader, gloader );
|
||
|
if (error) goto Exit;
|
||
|
|
||
|
slot->outline = slot->loader->base.outline;
|
||
|
slot->format = ft_glyph_format_outline;
|
||
|
}
|
||
|
|
||
|
Exit:
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* load and hint a given glyph */
|
||
|
FT_Error ah_hinter_load_glyph( AH_Hinter* hinter,
|
||
|
FT_GlyphSlot slot,
|
||
|
FT_Size size,
|
||
|
FT_UInt glyph_index,
|
||
|
FT_Int load_flags )
|
||
|
{
|
||
|
FT_Face face = slot->face;
|
||
|
FT_Error error;
|
||
|
FT_Fixed x_scale = size->metrics.x_scale;
|
||
|
FT_Fixed y_scale = size->metrics.y_scale;
|
||
|
AH_Face_Globals* face_globals = FACE_GLOBALS(face);
|
||
|
|
||
|
/* first of all, we need to check that we're using the correct face and */
|
||
|
/* global hints to load the glyph */
|
||
|
if ( hinter->face != face || hinter->globals != face_globals )
|
||
|
{
|
||
|
hinter->face = face;
|
||
|
if (!face_globals)
|
||
|
{
|
||
|
error = ah_hinter_new_face_globals( hinter, face, 0 );
|
||
|
if (error) goto Exit;
|
||
|
}
|
||
|
hinter->globals = FACE_GLOBALS(face);
|
||
|
face_globals = FACE_GLOBALS(face);
|
||
|
}
|
||
|
|
||
|
/* now, we must check the current character pixel size to see if we need */
|
||
|
/* to rescale the global metrics.. */
|
||
|
if ( face_globals->x_scale != x_scale ||
|
||
|
face_globals->y_scale != y_scale )
|
||
|
{
|
||
|
ah_hinter_scale_globals( hinter, x_scale, y_scale );
|
||
|
}
|
||
|
|
||
|
load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE;
|
||
|
|
||
|
ah_loader_rewind( hinter->loader );
|
||
|
|
||
|
error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
|
||
|
|
||
|
Exit:
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* retrieve a face's autohint globals for client applications */
|
||
|
void ah_hinter_get_global_hints( AH_Hinter* hinter,
|
||
|
FT_Face face,
|
||
|
void* *global_hints,
|
||
|
long *global_len )
|
||
|
{
|
||
|
AH_Globals* globals = 0;
|
||
|
FT_Memory memory = hinter->memory;
|
||
|
FT_Error error;
|
||
|
|
||
|
/* allocate new master globals */
|
||
|
if (ALLOC( globals, sizeof(*globals)))
|
||
|
goto Fail;
|
||
|
|
||
|
/* compute face globals if needed */
|
||
|
if (!FACE_GLOBALS(face))
|
||
|
{
|
||
|
error = ah_hinter_new_face_globals( hinter, face, 0 );
|
||
|
if (error) goto Fail;
|
||
|
}
|
||
|
|
||
|
*globals = FACE_GLOBALS(face)->design;
|
||
|
*global_hints = globals;
|
||
|
*global_len = sizeof(*globals);
|
||
|
return;
|
||
|
|
||
|
Fail:
|
||
|
FREE( globals );
|
||
|
*global_hints = 0;
|
||
|
*global_len = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
void ah_hinter_done_global_hints( AH_Hinter* hinter,
|
||
|
void* global_hints )
|
||
|
{
|
||
|
FT_Memory memory = hinter->memory;
|
||
|
FREE( global_hints );
|
||
|
}
|
||
|
|
||
|
|