blob: 0d2b475abd135fb2b4332db98d326c567fbad30c [file] [log] [blame]
David Turner3469d0d2000-07-19 20:02:14 +00001/***************************************************************************/
2/* */
3/* ahhint.c */
4/* */
Werner Lembergc3dd1512000-07-26 14:11:15 +00005/* Glyph hinter (body). */
David Turner3469d0d2000-07-19 20:02:14 +00006/* */
Werner Lembergc3dd1512000-07-26 14:11:15 +00007/* Copyright 2000 Catharon Productions Inc. */
David Turner3469d0d2000-07-19 20:02:14 +00008/* Author: David Turner */
9/* */
10/* This file is part of the Catharon Typography Project and shall only */
11/* be used, modified, and distributed under the terms of the Catharon */
12/* Open Source License that should come with this file under the name */
Werner Lembergc3dd1512000-07-26 14:11:15 +000013/* `CatharonLicense.txt'. By continuing to use, modify, or distribute */
David Turner3469d0d2000-07-19 20:02:14 +000014/* this file you indicate that you have read the license and */
15/* understand and accept it fully. */
16/* */
Werner Lembergc3dd1512000-07-26 14:11:15 +000017/* Note that this license is compatible with the FreeType license. */
David Turner3469d0d2000-07-19 20:02:14 +000018/* */
19/***************************************************************************/
20
Werner Lembergc3dd1512000-07-26 14:11:15 +000021
David Turner3469d0d2000-07-19 20:02:14 +000022#ifdef FT_FLAT_COMPILE
Werner Lembergc3dd1512000-07-26 14:11:15 +000023
David Turner3469d0d2000-07-19 20:02:14 +000024#include "ahhint.h"
25#include "ahglyph.h"
26#include "ahangles.h"
Werner Lembergc3dd1512000-07-26 14:11:15 +000027
David Turner3469d0d2000-07-19 20:02:14 +000028#else
Werner Lembergc3dd1512000-07-26 14:11:15 +000029
David Turner3469d0d2000-07-19 20:02:14 +000030#include <autohint/ahhint.h>
31#include <autohint/ahglyph.h>
32#include <autohint/ahangles.h>
Werner Lembergc3dd1512000-07-26 14:11:15 +000033
David Turner3469d0d2000-07-19 20:02:14 +000034#endif
35
36#include <freetype/ftoutln.h>
37
Werner Lembergc3dd1512000-07-26 14:11:15 +000038
39#define FACE_GLOBALS( face ) ((AH_Face_Globals*)(face)->autohint.data)
David Turner3469d0d2000-07-19 20:02:14 +000040
41#define AH_USE_IUP
42
43
Werner Lembergc3dd1512000-07-26 14:11:15 +000044 /*************************************************************************/
45 /*************************************************************************/
46 /**** ****/
47 /**** Hinting routines ****/
48 /**** ****/
49 /*************************************************************************/
50 /*************************************************************************/
51
David Turner3469d0d2000-07-19 20:02:14 +000052
53 static int disable_horz_edges = 0;
54 static int disable_vert_edges = 0;
55
Werner Lembergc3dd1512000-07-26 14:11:15 +000056
David Turner3469d0d2000-07-19 20:02:14 +000057 /* snap a given width in scaled coordinates to one of the */
Werner Lembergc3dd1512000-07-26 14:11:15 +000058 /* current standard widths */
David Turner3469d0d2000-07-19 20:02:14 +000059 static
Werner Lembergc3dd1512000-07-26 14:11:15 +000060 FT_Pos ah_snap_width( FT_Pos* widths,
61 FT_Int count,
62 FT_Pos width )
David Turner3469d0d2000-07-19 20:02:14 +000063 {
64 int n;
Werner Lembergc3dd1512000-07-26 14:11:15 +000065 FT_Pos best = 64 + 32 + 2;
David Turner3469d0d2000-07-19 20:02:14 +000066 FT_Pos reference = width;
67
Werner Lembergc3dd1512000-07-26 14:11:15 +000068
David Turner3469d0d2000-07-19 20:02:14 +000069 for ( n = 0; n < count; n++ )
70 {
71 FT_Pos w;
72 FT_Pos dist;
73
Werner Lembergc3dd1512000-07-26 14:11:15 +000074
David Turner3469d0d2000-07-19 20:02:14 +000075 w = widths[n];
76 dist = width - w;
Werner Lembergc3dd1512000-07-26 14:11:15 +000077 if ( dist < 0 )
78 dist = -dist;
79 if ( dist < best )
David Turner3469d0d2000-07-19 20:02:14 +000080 {
81 best = dist;
82 reference = w;
83 }
84 }
85
86 if ( width >= reference )
87 {
88 width -= 0x21;
89 if ( width < reference )
90 width = reference;
91 }
92 else
93 {
94 width += 0x21;
95 if ( width > reference )
96 width = reference;
97 }
98
99 return width;
100 }
101
102
Werner Lembergc3dd1512000-07-26 14:11:15 +0000103 /* align one stem edge relative to the previous stem edge */
David Turner3469d0d2000-07-19 20:02:14 +0000104 static
105 void ah_align_linked_edge( AH_Hinter* hinter,
106 AH_Edge* base_edge,
107 AH_Edge* stem_edge,
108 int vertical )
109 {
110 FT_Pos dist = stem_edge->opos - base_edge->opos;
111 AH_Globals* globals = &hinter->globals->scaled;
112 FT_Pos sign = 1;
113
Werner Lembergc3dd1512000-07-26 14:11:15 +0000114
115 if ( dist < 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000116 {
117 dist = -dist;
118 sign = -1;
119 }
120
Werner Lembergc3dd1512000-07-26 14:11:15 +0000121 if ( vertical )
David Turner3469d0d2000-07-19 20:02:14 +0000122 {
123 dist = ah_snap_width( globals->heights, globals->num_heights, dist );
124
125 /* in the case of vertical hinting, always round */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000126 /* the stem heights to integer pixels */
127 if ( dist >= 64 )
128 dist = ( dist + 16 ) & -64;
David Turner3469d0d2000-07-19 20:02:14 +0000129 else
130 dist = 64;
131 }
132 else
133 {
134 dist = ah_snap_width( globals->widths, globals->num_widths, dist );
135
Werner Lembergc3dd1512000-07-26 14:11:15 +0000136 if ( hinter->flags & ah_hinter_monochrome )
David Turner3469d0d2000-07-19 20:02:14 +0000137 {
138 /* monochrome horizontal hinting: snap widths to integer pixels */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000139 /* with a different threshold */
140 if ( dist < 64 )
David Turner3469d0d2000-07-19 20:02:14 +0000141 dist = 64;
142 else
Werner Lembergc3dd1512000-07-26 14:11:15 +0000143 dist = ( dist + 32 ) & -64;
David Turner3469d0d2000-07-19 20:02:14 +0000144 }
145 else
146 {
147 /* for horizontal anti-aliased hinting, we adopt a more subtle */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000148 /* approach: we strengthen small stems, round stems whose size */
David Turner3469d0d2000-07-19 20:02:14 +0000149 /* is between 1 and 2 pixels to an integer, otherwise nothing */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000150 if ( dist < 48 )
151 dist = ( dist + 64 ) >> 1;
David Turner3469d0d2000-07-19 20:02:14 +0000152
Werner Lembergc3dd1512000-07-26 14:11:15 +0000153 else if ( dist < 128 )
154 dist = ( dist + 42 ) & -64;
David Turner3469d0d2000-07-19 20:02:14 +0000155 }
156 }
David Turner3469d0d2000-07-19 20:02:14 +0000157
Werner Lembergc3dd1512000-07-26 14:11:15 +0000158 stem_edge->pos = base_edge->pos + sign * dist;
159 }
David Turner3469d0d2000-07-19 20:02:14 +0000160
161
162 static
163 void ah_align_serif_edge( AH_Hinter* hinter,
164 AH_Edge* base,
165 AH_Edge* serif )
166 {
167 FT_Pos dist;
168 FT_Pos sign = 1;
169
Werner Lembergc3dd1512000-07-26 14:11:15 +0000170 UNUSED( hinter );
171
172
David Turner3469d0d2000-07-19 20:02:14 +0000173 dist = serif->opos - base->opos;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000174 if ( dist < 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000175 {
176 dist = -dist;
177 sign = -1;
178 }
179
David Turner3469d0d2000-07-19 20:02:14 +0000180 /* do not strengthen serifs */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000181 if ( base->flags & ah_edge_done )
David Turner3469d0d2000-07-19 20:02:14 +0000182 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000183 if ( dist > 64 )
184 dist = ( dist + 16 ) & -64;
David Turner3469d0d2000-07-19 20:02:14 +0000185
Werner Lembergc3dd1512000-07-26 14:11:15 +0000186 else if ( dist <= 32 )
187 dist = ( dist + 33 ) >> 1;
David Turner3469d0d2000-07-19 20:02:14 +0000188 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000189
190 serif->pos = base->pos + sign * dist;
David Turner3469d0d2000-07-19 20:02:14 +0000191 }
192
193
Werner Lembergc3dd1512000-07-26 14:11:15 +0000194 /*************************************************************************/
195 /*************************************************************************/
196 /*************************************************************************/
197 /**** ****/
198 /**** E D G E H I N T I N G ****/
199 /**** ****/
200 /*************************************************************************/
201 /*************************************************************************/
202 /*************************************************************************/
203
David Turner3469d0d2000-07-19 20:02:14 +0000204
205 /* Another alternative edge hinting algorithm */
206 static
207 void ah_hint_edges_3( AH_Hinter* hinter )
208 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000209 AH_Edge* edges;
210 AH_Edge* edge_limit;
211 AH_Outline* outline = hinter->glyph;
212 FT_Int dimension;
213
David Turner3469d0d2000-07-19 20:02:14 +0000214
215 edges = outline->horz_edges;
216 edge_limit = edges + outline->num_hedges;
217
218 for ( dimension = 1; dimension >= 0; dimension-- )
219 {
220 AH_Edge* edge;
221 AH_Edge* before = 0;
222 AH_Edge* after = 0;
223 AH_Edge* anchor = 0;
224 int has_serifs = 0;
225
Werner Lembergc3dd1512000-07-26 14:11:15 +0000226
227 if ( disable_vert_edges && !dimension )
David Turner3469d0d2000-07-19 20:02:14 +0000228 goto Next_Dimension;
229
Werner Lembergc3dd1512000-07-26 14:11:15 +0000230 if ( disable_horz_edges && dimension )
David Turner3469d0d2000-07-19 20:02:14 +0000231 goto Next_Dimension;
232
233 /* we begin by aligning all stems relative to the blue zone */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000234 /* if needed -- that's only for horizontal edges */
235 if ( dimension )
David Turner3469d0d2000-07-19 20:02:14 +0000236 {
237 for ( edge = edges; edge < edge_limit; edge++ )
238 {
239 FT_Pos* blue;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000240 AH_Edge *edge1, *edge2;
David Turner3469d0d2000-07-19 20:02:14 +0000241
Werner Lembergc3dd1512000-07-26 14:11:15 +0000242
243 if ( edge->flags & ah_edge_done )
David Turner3469d0d2000-07-19 20:02:14 +0000244 continue;
245
246 blue = edge->blue_edge;
247 edge1 = 0;
248 edge2 = edge->link;
249
Werner Lembergc3dd1512000-07-26 14:11:15 +0000250 if ( blue )
David Turner3469d0d2000-07-19 20:02:14 +0000251 {
252 edge1 = edge;
253 }
254 else if (edge2 && edge2->blue_edge)
255 {
256 blue = edge2->blue_edge;
257 edge1 = edge2;
258 edge2 = edge;
259 }
260
Werner Lembergc3dd1512000-07-26 14:11:15 +0000261 if ( !edge1 )
David Turner3469d0d2000-07-19 20:02:14 +0000262 continue;
263
264 edge1->pos = blue[0];
265 edge1->flags |= ah_edge_done;
266
Werner Lembergc3dd1512000-07-26 14:11:15 +0000267 if ( edge2 && !edge2->blue_edge )
David Turner3469d0d2000-07-19 20:02:14 +0000268 {
269 ah_align_linked_edge( hinter, edge1, edge2, dimension );
270 edge2->flags |= ah_edge_done;
271 }
272
Werner Lembergc3dd1512000-07-26 14:11:15 +0000273 if ( !anchor )
David Turner3469d0d2000-07-19 20:02:14 +0000274 anchor = edge;
275 }
276 }
277
278 /* now, we will align all stem edges, trying to maintain the */
279 /* relative order of stems in the glyph.. */
280 before = 0;
281 after = 0;
282 for ( edge = edges; edge < edge_limit; edge++ )
283 {
284 AH_Edge *edge2;
285
Werner Lembergc3dd1512000-07-26 14:11:15 +0000286
287 if ( edge->flags & ah_edge_done )
David Turner3469d0d2000-07-19 20:02:14 +0000288 continue;
289
290 /* skip all non-stem edges */
291 edge2 = edge->link;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000292 if ( !edge2 )
David Turner3469d0d2000-07-19 20:02:14 +0000293 {
294 has_serifs++;
295 continue;
296 }
297
298 /* now, align the stem */
299
300 /* this should not happen, but it's better to be safe.. */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000301 if ( edge2->blue_edge || edge2 < edge )
David Turner3469d0d2000-07-19 20:02:14 +0000302 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000303
David Turner3469d0d2000-07-19 20:02:14 +0000304#if 0
305 printf( "strange blue alignement, edge %d to %d\n",
Werner Lembergc3dd1512000-07-26 14:11:15 +0000306 edge - edges, edge2 - edges );
David Turner3469d0d2000-07-19 20:02:14 +0000307#endif
Werner Lembergc3dd1512000-07-26 14:11:15 +0000308
David Turner3469d0d2000-07-19 20:02:14 +0000309 ah_align_linked_edge( hinter, edge2, edge, dimension );
310 edge->flags |= ah_edge_done;
311 continue;
312 }
313
314 {
315 FT_Bool min = 0;
316 FT_Pos delta;
317
Werner Lembergc3dd1512000-07-26 14:11:15 +0000318 if ( !anchor )
David Turner3469d0d2000-07-19 20:02:14 +0000319 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000320 edge->pos = ( edge->opos + 32 ) & -64;
David Turner3469d0d2000-07-19 20:02:14 +0000321 anchor = edge;
322 }
323 else
Werner Lembergc3dd1512000-07-26 14:11:15 +0000324 edge->pos = anchor->pos +
325 ( ( edge->opos - anchor->opos + 32 ) & -64 );
David Turner3469d0d2000-07-19 20:02:14 +0000326
327 edge->flags |= ah_edge_done;
328
Werner Lembergc3dd1512000-07-26 14:11:15 +0000329 if ( edge > edges && edge->pos < edge[-1].pos )
David Turner3469d0d2000-07-19 20:02:14 +0000330 {
331 edge->pos = edge[-1].pos;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000332 min = 1;
David Turner3469d0d2000-07-19 20:02:14 +0000333 }
334
335 ah_align_linked_edge( hinter, edge, edge2, dimension );
336 delta = 0;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000337 if ( edge2 + 1 < edge_limit &&
David Turner3469d0d2000-07-19 20:02:14 +0000338 edge2[1].flags & ah_edge_done )
339 delta = edge2[1].pos - edge2->pos;
340
Werner Lembergc3dd1512000-07-26 14:11:15 +0000341 if ( delta < 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000342 {
343 edge2->pos += delta;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000344 if ( !min )
David Turner3469d0d2000-07-19 20:02:14 +0000345 edge->pos += delta;
346 }
347 edge2->flags |= ah_edge_done;
348 }
349 }
350
Werner Lembergc3dd1512000-07-26 14:11:15 +0000351 if ( !has_serifs )
David Turner3469d0d2000-07-19 20:02:14 +0000352 goto Next_Dimension;
353
354 /* now, hint the remaining edges (serifs and single) in order */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000355 /* to complete our processing */
David Turner3469d0d2000-07-19 20:02:14 +0000356 for ( edge = edges; edge < edge_limit; edge++ )
357 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000358 if ( edge->flags & ah_edge_done )
David Turner3469d0d2000-07-19 20:02:14 +0000359 continue;
360
Werner Lembergc3dd1512000-07-26 14:11:15 +0000361 if ( edge->serif )
David Turner3469d0d2000-07-19 20:02:14 +0000362 {
363 ah_align_serif_edge( hinter, edge->serif, edge );
364 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000365 else if ( !anchor )
David Turner3469d0d2000-07-19 20:02:14 +0000366 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000367 edge->pos = ( edge->opos + 32 ) & -64;
David Turner3469d0d2000-07-19 20:02:14 +0000368 anchor = edge;
369 }
370 else
Werner Lembergc3dd1512000-07-26 14:11:15 +0000371 edge->pos = anchor->pos +
372 ( ( edge->opos-anchor->opos + 32 ) & -64 );
David Turner3469d0d2000-07-19 20:02:14 +0000373
374 edge->flags |= ah_edge_done;
375
Werner Lembergc3dd1512000-07-26 14:11:15 +0000376 if ( edge > edges && edge->pos < edge[-1].pos )
David Turner3469d0d2000-07-19 20:02:14 +0000377 edge->pos = edge[-1].pos;
378
Werner Lembergc3dd1512000-07-26 14:11:15 +0000379 if ( edge + 1 < edge_limit &&
380 edge[1].flags & ah_edge_done &&
381 edge->pos > edge[1].pos )
David Turner3469d0d2000-07-19 20:02:14 +0000382 edge->pos = edge[1].pos;
383 }
384
385 Next_Dimension:
386 edges = outline->vert_edges;
387 edge_limit = edges + outline->num_vedges;
388 }
David Turner3469d0d2000-07-19 20:02:14 +0000389 }
390
391
David Turner76a5f622000-11-04 01:55:49 +0000392 FT_LOCAL_DEF
David Turner3469d0d2000-07-19 20:02:14 +0000393 void ah_hinter_hint_edges( AH_Hinter* hinter,
394 int no_horz_edges,
395 int no_vert_edges )
396 {
397 disable_horz_edges = no_horz_edges;
398 disable_vert_edges = no_vert_edges;
399
Werner Lembergc3dd1512000-07-26 14:11:15 +0000400 /* AH_Interpolate_Blue_Edges( hinter ); -- doesn't seem to help */
401 /* reduce the problem of the disappearing eye in the `e' of Times... */
402 /* also, creates some artifacts near the blue zones? */
David Turner3469d0d2000-07-19 20:02:14 +0000403 {
404 ah_hint_edges_3( hinter );
405
David Turner3469d0d2000-07-19 20:02:14 +0000406#if 0
Werner Lembergc3dd1512000-07-26 14:11:15 +0000407 /* outline optimizer removed temporarily */
408 if ( hinter->flags & ah_hinter_optimize )
David Turner3469d0d2000-07-19 20:02:14 +0000409 {
410 AH_Optimizer opt;
411
Werner Lembergc3dd1512000-07-26 14:11:15 +0000412
413 if ( !AH_Optimizer_Init( &opt, hinter->glyph, hinter->memory ) )
David Turner3469d0d2000-07-19 20:02:14 +0000414 {
415 AH_Optimizer_Compute( &opt );
416 AH_Optimizer_Done( &opt );
417 }
418 }
419#endif
Werner Lembergc3dd1512000-07-26 14:11:15 +0000420
David Turner3469d0d2000-07-19 20:02:14 +0000421 }
422 }
423
424
Werner Lembergc3dd1512000-07-26 14:11:15 +0000425 /*************************************************************************/
426 /*************************************************************************/
427 /*************************************************************************/
428 /**** ****/
429 /**** P O I N T H I N T I N G ****/
430 /**** ****/
431 /*************************************************************************/
432 /*************************************************************************/
433 /*************************************************************************/
David Turner3469d0d2000-07-19 20:02:14 +0000434
435 static
436 void ah_hinter_align_edge_points( AH_Hinter* hinter )
437 {
438 AH_Outline* outline = hinter->glyph;
439 AH_Edge* edges;
440 AH_Edge* edge_limit;
441 FT_Int dimension;
442
Werner Lembergc3dd1512000-07-26 14:11:15 +0000443
444 edges = outline->horz_edges;
445 edge_limit = edges + outline->num_hedges;
David Turner3469d0d2000-07-19 20:02:14 +0000446
447 for ( dimension = 1; dimension >= 0; dimension-- )
448 {
449 AH_Edge* edge;
450 AH_Edge* before;
451 AH_Edge* after;
452
Werner Lembergc3dd1512000-07-26 14:11:15 +0000453
David Turner3469d0d2000-07-19 20:02:14 +0000454 before = 0;
455 after = 0;
456
457 edge = edges;
458 for ( ; edge < edge_limit; edge++ )
459 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000460 /* move the points of each segment */
461 /* in each edge to the edge's position */
David Turner3469d0d2000-07-19 20:02:14 +0000462 AH_Segment* seg = edge->first;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000463
464
David Turner3469d0d2000-07-19 20:02:14 +0000465 do
466 {
467 AH_Point* point = seg->first;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000468
469
David Turner3469d0d2000-07-19 20:02:14 +0000470 for (;;)
471 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000472 if ( dimension )
David Turner3469d0d2000-07-19 20:02:14 +0000473 {
474 point->y = edge->pos;
475 point->flags |= ah_flah_touch_y;
476 }
477 else
478 {
479 point->x = edge->pos;
480 point->flags |= ah_flah_touch_x;
481 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000482
483 if ( point == seg->last )
David Turner3469d0d2000-07-19 20:02:14 +0000484 break;
485
486 point = point->next;
487 }
488
489 seg = seg->edge_next;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000490
491 } while ( seg != edge->first );
David Turner3469d0d2000-07-19 20:02:14 +0000492 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000493
David Turner3469d0d2000-07-19 20:02:14 +0000494 edges = outline->vert_edges;
495 edge_limit = edges + outline->num_vedges;
496 }
497 }
498
499
Werner Lembergc3dd1512000-07-26 14:11:15 +0000500 /* hint the strong points -- this is equivalent to the TrueType `IP' */
David Turner3469d0d2000-07-19 20:02:14 +0000501 static
502 void ah_hinter_align_strong_points( AH_Hinter* hinter )
503 {
504 AH_Outline* outline = hinter->glyph;
505 FT_Int dimension;
506 AH_Edge* edges;
507 AH_Edge* edge_limit;
508 AH_Point* points;
509 AH_Point* point_limit;
510 AH_Flags touch_flag;
511
Werner Lembergc3dd1512000-07-26 14:11:15 +0000512
David Turner3469d0d2000-07-19 20:02:14 +0000513 points = outline->points;
514 point_limit = points + outline->num_points;
515
516 edges = outline->horz_edges;
517 edge_limit = edges + outline->num_hedges;
518 touch_flag = ah_flah_touch_y;
519
520 for ( dimension = 1; dimension >= 0; dimension-- )
521 {
522 AH_Point* point;
523 AH_Edge* edge;
David Turner3469d0d2000-07-19 20:02:14 +0000524
Werner Lembergc3dd1512000-07-26 14:11:15 +0000525
David Turner3469d0d2000-07-19 20:02:14 +0000526 if ( edges < edge_limit )
527 for ( point = points; point < point_limit; point++ )
528 {
529 FT_Pos u, ou, fu; /* point position */
530 FT_Pos delta;
531
Werner Lembergc3dd1512000-07-26 14:11:15 +0000532
David Turner3469d0d2000-07-19 20:02:14 +0000533 if ( point->flags & touch_flag )
534 continue;
535
536#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
Werner Lembergc3dd1512000-07-26 14:11:15 +0000537 /* if this point is candidate to weak interpolation, we will */
David Turner3469d0d2000-07-19 20:02:14 +0000538 /* interpolate it after all strong points have been processed */
539 if ( point->flags & ah_flah_weak_interpolation )
540 continue;
541#endif
542
Werner Lembergc3dd1512000-07-26 14:11:15 +0000543 if ( dimension )
544 {
545 u = point->fy;
546 ou = point->oy;
547 }
548 else
549 {
550 u = point->fx;
551 ou = point->ox;
552 }
David Turner3469d0d2000-07-19 20:02:14 +0000553
554 fu = u;
555
Werner Lembergc3dd1512000-07-26 14:11:15 +0000556 /* is the point before the first edge? */
David Turner3469d0d2000-07-19 20:02:14 +0000557 edge = edges;
558 delta = edge->fpos - u;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000559 if ( delta >= 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000560 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000561 u = edge->pos - ( edge->opos - ou );
David Turner3469d0d2000-07-19 20:02:14 +0000562 goto Store_Point;
563 }
564
565 /* is the point after the last edge ? */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000566 edge = edge_limit - 1;
David Turner3469d0d2000-07-19 20:02:14 +0000567 delta = u - edge->fpos;
568 if ( delta >= 0 )
569 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000570 u = edge->pos + ( ou - edge->opos );
David Turner3469d0d2000-07-19 20:02:14 +0000571 goto Store_Point;
572 }
573
574 /* otherwise, interpolate the point in between */
575 {
576 AH_Edge* before = 0;
577 AH_Edge* after = 0;
578
Werner Lembergc3dd1512000-07-26 14:11:15 +0000579
David Turner3469d0d2000-07-19 20:02:14 +0000580 for ( edge = edges; edge < edge_limit; edge++ )
581 {
582 if ( u == edge->fpos )
583 {
584 u = edge->pos;
585 goto Store_Point;
586 }
587 if ( u < edge->fpos )
588 break;
589 before = edge;
590 }
591
Werner Lembergc3dd1512000-07-26 14:11:15 +0000592 for ( edge = edge_limit - 1; edge >= edges; edge-- )
David Turner3469d0d2000-07-19 20:02:14 +0000593 {
594 if ( u == edge->fpos )
595 {
596 u = edge->pos;
597 goto Store_Point;
598 }
599 if ( u > edge->fpos )
600 break;
601 after = edge;
602 }
603
604 /* assert( before && after && before != after ) */
605 u = before->pos + FT_MulDiv( fu - before->fpos,
606 after->pos - before->pos,
607 after->fpos - before->fpos );
608 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000609
David Turner3469d0d2000-07-19 20:02:14 +0000610 Store_Point:
611
612 /* save the point position */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000613 if ( dimension )
614 point->y = u;
615 else
616 point->x = u;
David Turner3469d0d2000-07-19 20:02:14 +0000617
618 point->flags |= touch_flag;
619 }
620
621 edges = outline->vert_edges;
622 edge_limit = edges + outline->num_vedges;
623 touch_flag = ah_flah_touch_x;
624 }
625 }
626
627
628#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
Werner Lembergc3dd1512000-07-26 14:11:15 +0000629
David Turner3469d0d2000-07-19 20:02:14 +0000630 static
631 void ah_iup_shift( AH_Point* p1,
632 AH_Point* p2,
633 AH_Point* ref )
634 {
635 AH_Point* p;
636 FT_Pos delta = ref->u - ref->v;
637
Werner Lembergc3dd1512000-07-26 14:11:15 +0000638
David Turner3469d0d2000-07-19 20:02:14 +0000639 for ( p = p1; p < ref; p++ )
640 p->u = p->v + delta;
641
Werner Lembergc3dd1512000-07-26 14:11:15 +0000642 for ( p = ref + 1; p <= p2; p++ )
David Turner3469d0d2000-07-19 20:02:14 +0000643 p->u = p->v + delta;
644 }
645
646
647 static
648 void ah_iup_interp( AH_Point* p1,
649 AH_Point* p2,
650 AH_Point* ref1,
651 AH_Point* ref2 )
652 {
653 AH_Point* p;
654 FT_Pos u;
655 FT_Pos v1 = ref1->v;
656 FT_Pos v2 = ref2->v;
657 FT_Pos d1 = ref1->u - v1;
658 FT_Pos d2 = ref2->u - v2;
659
David Turner3469d0d2000-07-19 20:02:14 +0000660
Werner Lembergc3dd1512000-07-26 14:11:15 +0000661 if ( p1 > p2 )
662 return;
663
664 if ( v1 == v2 )
David Turner3469d0d2000-07-19 20:02:14 +0000665 {
666 for ( p = p1; p <= p2; p++ )
667 {
Werner Lemberge72c9fe2000-07-31 18:59:02 +0000668 u = p->v;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000669
670 if ( u <= v1 )
671 u += d1;
672 else
673 u += d2;
674
David Turner3469d0d2000-07-19 20:02:14 +0000675 p->u = u;
676 }
677 return;
678 }
679
680 if ( v1 < v2 )
681 {
682 for ( p = p1; p <= p2; p++ )
683 {
684 u = p->v;
685
Werner Lembergc3dd1512000-07-26 14:11:15 +0000686 if ( u <= v1 )
687 u += d1;
688 else if ( u >= v2 )
689 u += d2;
690 else
691 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
David Turner3469d0d2000-07-19 20:02:14 +0000692
693 p->u = u;
694 }
695 }
696 else
697 {
698 for ( p = p1; p <= p2; p++ )
699 {
700 u = p->v;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000701
702 if ( u <= v2 )
703 u += d2;
704 else if ( u >= v1 )
705 u += d1;
706 else
707 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
David Turner3469d0d2000-07-19 20:02:14 +0000708
709 p->u = u;
710 }
711 }
712 }
713
Werner Lembergc3dd1512000-07-26 14:11:15 +0000714
715 /* interpolate weak points -- this is equivalent to the TrueType `IUP' */
David Turner3469d0d2000-07-19 20:02:14 +0000716 static
717 void ah_hinter_align_weak_points( AH_Hinter* hinter )
718 {
719 AH_Outline* outline = hinter->glyph;
720 FT_Int dimension;
721 AH_Edge* edges;
722 AH_Edge* edge_limit;
723 AH_Point* points;
724 AH_Point* point_limit;
725 AH_Point** contour_limit;
726 AH_Flags touch_flag;
727
Werner Lembergc3dd1512000-07-26 14:11:15 +0000728
David Turner3469d0d2000-07-19 20:02:14 +0000729 points = outline->points;
730 point_limit = points + outline->num_points;
731
Werner Lembergc3dd1512000-07-26 14:11:15 +0000732 /* PASS 1: Move segment points to edge positions */
David Turner3469d0d2000-07-19 20:02:14 +0000733
734 edges = outline->horz_edges;
735 edge_limit = edges + outline->num_hedges;
736 touch_flag = ah_flah_touch_y;
737
738 contour_limit = outline->contours + outline->num_contours;
739
740 ah_setup_uv( outline, ah_uv_oy );
741
742 for ( dimension = 1; dimension >= 0; dimension-- )
743 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000744 AH_Point* point;
745 AH_Point* end_point;
746 AH_Point* first_point;
747 AH_Point** contour;
748
David Turner3469d0d2000-07-19 20:02:14 +0000749
750 point = points;
751 contour = outline->contours;
752
753 for ( ; contour < contour_limit; contour++ )
754 {
755 point = *contour;
756 end_point = point->prev;
757 first_point = point;
758
Werner Lembergc3dd1512000-07-26 14:11:15 +0000759 while ( point <= end_point && !( point->flags & touch_flag ) )
David Turner3469d0d2000-07-19 20:02:14 +0000760 point++;
761
Werner Lembergc3dd1512000-07-26 14:11:15 +0000762 if ( point <= end_point )
David Turner3469d0d2000-07-19 20:02:14 +0000763 {
764 AH_Point* first_touched = point;
765 AH_Point* cur_touched = point;
766
Werner Lembergc3dd1512000-07-26 14:11:15 +0000767
David Turner3469d0d2000-07-19 20:02:14 +0000768 point++;
769 while ( point <= end_point )
770 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000771 if ( point->flags & touch_flag )
David Turner3469d0d2000-07-19 20:02:14 +0000772 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000773 /* we found two successive touched points; we interpolate */
774 /* all contour points between them */
775 ah_iup_interp( cur_touched + 1, point - 1,
David Turner3469d0d2000-07-19 20:02:14 +0000776 cur_touched, point );
777 cur_touched = point;
778 }
779 point++;
780 }
781
Werner Lembergc3dd1512000-07-26 14:11:15 +0000782 if ( cur_touched == first_touched )
David Turner3469d0d2000-07-19 20:02:14 +0000783 {
784 /* this is a special case: only one point was touched in the */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000785 /* contour; we thus simply shift the whole contour */
David Turner3469d0d2000-07-19 20:02:14 +0000786 ah_iup_shift( first_point, end_point, cur_touched );
787 }
788 else
789 {
790 /* now interpolate after the last touched point to the end */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000791 /* of the contour */
792 ah_iup_interp( cur_touched + 1, end_point,
David Turner3469d0d2000-07-19 20:02:14 +0000793 cur_touched, first_touched );
794
Werner Lembergc3dd1512000-07-26 14:11:15 +0000795 /* if the first contour point isn't touched, interpolate */
796 /* from the contour start to the first touched point */
797 if ( first_touched > points )
798 ah_iup_interp( first_point, first_touched - 1,
David Turner3469d0d2000-07-19 20:02:14 +0000799 cur_touched, first_touched );
800 }
801 }
802 }
803
804 /* now save the interpolated values back to x/y */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000805 if ( dimension )
David Turner3469d0d2000-07-19 20:02:14 +0000806 {
807 for ( point = points; point < point_limit; point++ )
808 point->y = point->u;
809
810 touch_flag = ah_flah_touch_x;
811 ah_setup_uv( outline, ah_uv_ox );
812 }
813 else
814 {
815 for ( point = points; point < point_limit; point++ )
816 point->x = point->u;
817
818 break; /* exit loop */
819 }
820 }
821 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000822
823#endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */
824
David Turner3469d0d2000-07-19 20:02:14 +0000825
David Turner76a5f622000-11-04 01:55:49 +0000826 FT_LOCAL_DEF
David Turner3469d0d2000-07-19 20:02:14 +0000827 void ah_hinter_align_points( AH_Hinter* hinter )
828 {
829 ah_hinter_align_edge_points( hinter );
830
831#ifndef AH_OPTION_NO_STRONG_INTERPOLATION
832 ah_hinter_align_strong_points( hinter );
833#endif
834
835#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
836 ah_hinter_align_weak_points( hinter );
837#endif
838 }
839
840
Werner Lembergc3dd1512000-07-26 14:11:15 +0000841 /*************************************************************************/
842 /*************************************************************************/
843 /*************************************************************************/
844 /**** ****/
845 /**** H I N T E R O B J E C T M E T H O D S ****/
846 /**** ****/
847 /*************************************************************************/
848 /*************************************************************************/
849 /*************************************************************************/
850
David Turner3469d0d2000-07-19 20:02:14 +0000851
852 /* scale and fit the global metrics */
853 static
854 void ah_hinter_scale_globals( AH_Hinter* hinter,
855 FT_Fixed x_scale,
856 FT_Fixed y_scale )
857 {
858 FT_Int n;
859 AH_Face_Globals* globals = hinter->globals;
860 AH_Globals* design = &globals->design;
861 AH_Globals* scaled = &globals->scaled;
862
Werner Lembergc3dd1512000-07-26 14:11:15 +0000863
David Turner3469d0d2000-07-19 20:02:14 +0000864 /* copy content */
865 *scaled = *design;
866
867 /* scale the standard widths & heights */
868 for ( n = 0; n < design->num_widths; n++ )
869 scaled->widths[n] = FT_MulFix( design->widths[n], x_scale );
870
871 for ( n = 0; n < design->num_heights; n++ )
872 scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
873
874 /* scale the blue zones */
875 for ( n = 0; n < ah_blue_max; n++ )
876 {
877 FT_Pos delta, delta2;
878
Werner Lembergc3dd1512000-07-26 14:11:15 +0000879
David Turner3469d0d2000-07-19 20:02:14 +0000880 delta = design->blue_shoots[n] - design->blue_refs[n];
Werner Lembergc3dd1512000-07-26 14:11:15 +0000881 delta2 = delta;
882 if ( delta < 0 )
883 delta2 = -delta2;
David Turner3469d0d2000-07-19 20:02:14 +0000884 delta2 = FT_MulFix( delta2, y_scale );
885
Werner Lembergc3dd1512000-07-26 14:11:15 +0000886 if ( delta2 < 32 )
David Turner3469d0d2000-07-19 20:02:14 +0000887 delta2 = 0;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000888 else if ( delta2 < 64 )
889 delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & -32 );
David Turner3469d0d2000-07-19 20:02:14 +0000890 else
Werner Lembergc3dd1512000-07-26 14:11:15 +0000891 delta2 = ( delta2 + 32 ) & -64;
David Turner3469d0d2000-07-19 20:02:14 +0000892
Werner Lembergc3dd1512000-07-26 14:11:15 +0000893 if ( delta < 0 )
894 delta2 = -delta2;
David Turner3469d0d2000-07-19 20:02:14 +0000895
Werner Lembergc3dd1512000-07-26 14:11:15 +0000896 scaled->blue_refs[n] =
897 ( FT_MulFix( design->blue_refs[n], y_scale ) + 32 ) & -64;
David Turner3469d0d2000-07-19 20:02:14 +0000898 scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2;
899 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000900
David Turnerc49f69c2000-07-20 03:44:50 +0000901 globals->x_scale = x_scale;
902 globals->y_scale = y_scale;
David Turner3469d0d2000-07-19 20:02:14 +0000903 }
904
905
906 static
907 void ah_hinter_align( AH_Hinter* hinter )
908 {
909 ah_hinter_align_edge_points( hinter );
910 ah_hinter_align_points( hinter );
911 }
912
913
Werner Lembergc3dd1512000-07-26 14:11:15 +0000914 /* finalize a hinter object */
David Turner76a5f622000-11-04 01:55:49 +0000915 FT_LOCAL_DEF
David Turner3469d0d2000-07-19 20:02:14 +0000916 void ah_hinter_done( AH_Hinter* hinter )
917 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000918 if ( hinter )
David Turner3469d0d2000-07-19 20:02:14 +0000919 {
920 FT_Memory memory = hinter->memory;
921
Werner Lembergc3dd1512000-07-26 14:11:15 +0000922
David Turner3469d0d2000-07-19 20:02:14 +0000923 ah_loader_done( hinter->loader );
924 ah_outline_done( hinter->glyph );
925
Werner Lembergc3dd1512000-07-26 14:11:15 +0000926 /* note: the `globals' pointer is _not_ owned by the hinter */
927 /* but by the current face object, we don't need to */
928 /* release it */
David Turner3469d0d2000-07-19 20:02:14 +0000929 hinter->globals = 0;
930 hinter->face = 0;
931
Werner Lembergc3dd1512000-07-26 14:11:15 +0000932 FREE( hinter );
David Turner3469d0d2000-07-19 20:02:14 +0000933 }
934 }
935
936
Werner Lembergc3dd1512000-07-26 14:11:15 +0000937 /* create a new empty hinter object */
David Turner76a5f622000-11-04 01:55:49 +0000938 FT_LOCAL_DEF
Werner Lembergc3dd1512000-07-26 14:11:15 +0000939 FT_Error ah_hinter_new( FT_Library library,
940 AH_Hinter** ahinter )
David Turner3469d0d2000-07-19 20:02:14 +0000941 {
942 AH_Hinter* hinter = 0;
943 FT_Memory memory = library->memory;
944 FT_Error error;
945
Werner Lembergc3dd1512000-07-26 14:11:15 +0000946
David Turner3469d0d2000-07-19 20:02:14 +0000947 *ahinter = 0;
948
949 /* allocate object */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000950 if ( ALLOC( hinter, sizeof ( *hinter ) ) )
951 goto Exit;
David Turner3469d0d2000-07-19 20:02:14 +0000952
953 hinter->memory = memory;
954 hinter->flags = 0;
955
956 /* allocate outline and loader */
957 error = ah_outline_new( memory, &hinter->glyph ) ||
958 ah_loader_new ( memory, &hinter->loader ) ||
959 ah_loader_create_extra( hinter->loader );
Werner Lembergc3dd1512000-07-26 14:11:15 +0000960 if ( error )
961 goto Exit;
David Turner3469d0d2000-07-19 20:02:14 +0000962
963 *ahinter = hinter;
964
965 Exit:
Werner Lembergc3dd1512000-07-26 14:11:15 +0000966 if ( error )
David Turner3469d0d2000-07-19 20:02:14 +0000967 ah_hinter_done( hinter );
968
969 return error;
970 }
971
972
Werner Lembergc3dd1512000-07-26 14:11:15 +0000973 /* create a face's autohint globals */
David Turner76a5f622000-11-04 01:55:49 +0000974 FT_LOCAL_DEF
Werner Lembergc3dd1512000-07-26 14:11:15 +0000975 FT_Error ah_hinter_new_face_globals( AH_Hinter* hinter,
976 FT_Face face,
977 AH_Globals* globals )
David Turner3469d0d2000-07-19 20:02:14 +0000978 {
979 FT_Error error;
980 FT_Memory memory = hinter->memory;
981 AH_Face_Globals* face_globals;
982
Werner Lembergc3dd1512000-07-26 14:11:15 +0000983
984 if ( ALLOC( face_globals, sizeof ( *face_globals ) ) )
David Turner3469d0d2000-07-19 20:02:14 +0000985 goto Exit;
986
987 hinter->face = face;
988 hinter->globals = face_globals;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000989
990 if ( globals )
David Turner3469d0d2000-07-19 20:02:14 +0000991 face_globals->design = *globals;
992 else
993 ah_hinter_compute_globals( hinter );
994
995 face->autohint.data = face_globals;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000996 face->autohint.finalizer = (FT_Generic_Finalizer)
997 ah_hinter_done_face_globals;
David Turner3469d0d2000-07-19 20:02:14 +0000998 face_globals->face = face;
999
1000 Exit:
1001 return error;
1002 }
1003
1004
Werner Lembergc3dd1512000-07-26 14:11:15 +00001005 /* discard a face's autohint globals */
David Turner76a5f622000-11-04 01:55:49 +00001006 FT_LOCAL_DEF
David Turner3469d0d2000-07-19 20:02:14 +00001007 void ah_hinter_done_face_globals( AH_Face_Globals* globals )
1008 {
Werner Lembergc3dd1512000-07-26 14:11:15 +00001009 FT_Face face = globals->face;
1010 FT_Memory memory = face->memory;
1011
David Turner3469d0d2000-07-19 20:02:14 +00001012
1013 FREE( globals );
1014 }
1015
1016
Werner Lembergc3dd1512000-07-26 14:11:15 +00001017 static
1018 FT_Error ah_hinter_load( AH_Hinter* hinter,
1019 FT_UInt glyph_index,
1020 FT_UInt load_flags,
1021 FT_UInt depth )
1022 {
1023 FT_Face face = hinter->face;
1024 FT_GlyphSlot slot = face->glyph;
1025 FT_Fixed x_scale = face->size->metrics.x_scale;
1026 FT_Fixed y_scale = face->size->metrics.y_scale;
1027 FT_Glyph_Metrics metrics; /* temporary metrics */
1028 FT_Error error;
1029 AH_Outline* outline = hinter->glyph;
1030 AH_Loader* gloader = hinter->loader;
1031 FT_Bool no_horz_hints =
1032 ( load_flags & AH_HINT_NO_HORZ_EDGES ) != 0;
1033 FT_Bool no_vert_hints =
1034 ( load_flags & AH_HINT_NO_VERT_EDGES ) != 0;
David Turner3469d0d2000-07-19 20:02:14 +00001035
1036
Werner Lembergc3dd1512000-07-26 14:11:15 +00001037 /* load the glyph */
1038 error = FT_Load_Glyph( face, glyph_index, load_flags );
1039 if ( error )
1040 goto Exit;
David Turner3469d0d2000-07-19 20:02:14 +00001041
Werner Lembergc3dd1512000-07-26 14:11:15 +00001042 /* save current glyph metrics */
1043 metrics = slot->metrics;
David Turner3469d0d2000-07-19 20:02:14 +00001044
David Turnera6c747d2000-11-08 19:44:48 +00001045 /* set linear horizontal metrics */
1046 slot->linearHoriAdvance = slot->metrics.horiAdvance;
1047 slot->linearVertAdvance = slot->metrics.vertAdvance;
1048
Werner Lembergc3dd1512000-07-26 14:11:15 +00001049 switch ( slot->format )
1050 {
1051 case ft_glyph_format_outline:
David Turnere0066752000-11-16 19:06:28 +00001052
1053 /* translate glyph outline if we need to */
Werner Lemberg5c4f5eb2000-11-18 04:42:41 +00001054 if ( hinter->transformed )
David Turnere0066752000-11-16 19:06:28 +00001055 {
1056 FT_UInt n = slot->outline.n_points;
1057 FT_Vector* point = slot->outline.points;
1058
Werner Lemberg5c4f5eb2000-11-18 04:42:41 +00001059
David Turnere0066752000-11-16 19:06:28 +00001060 for ( ; n > 0; point++, n-- )
1061 {
1062 point->x += hinter->trans_delta.x;
1063 point->y += hinter->trans_delta.y;
1064 }
1065 }
1066
1067 /* copy the outline points in the loader's current */
Werner Lembergc3dd1512000-07-26 14:11:15 +00001068 /* extra points, which is used to keep original glyph coordinates */
1069 error = ah_loader_check_points( gloader, slot->outline.n_points + 2,
1070 slot->outline.n_contours );
1071 if ( error )
1072 goto Exit;
David Turner3469d0d2000-07-19 20:02:14 +00001073
Werner Lembergc3dd1512000-07-26 14:11:15 +00001074 MEM_Copy( gloader->current.extra_points, slot->outline.points,
1075 slot->outline.n_points * sizeof ( FT_Vector ) );
David Turner3469d0d2000-07-19 20:02:14 +00001076
Werner Lembergc3dd1512000-07-26 14:11:15 +00001077 MEM_Copy( gloader->current.outline.contours, slot->outline.contours,
1078 slot->outline.n_contours * sizeof ( short ) );
David Turner3469d0d2000-07-19 20:02:14 +00001079
Werner Lembergc3dd1512000-07-26 14:11:15 +00001080 MEM_Copy( gloader->current.outline.tags, slot->outline.tags,
1081 slot->outline.n_points * sizeof ( char ) );
David Turner3469d0d2000-07-19 20:02:14 +00001082
Werner Lembergc3dd1512000-07-26 14:11:15 +00001083 gloader->current.outline.n_points = slot->outline.n_points;
1084 gloader->current.outline.n_contours = slot->outline.n_contours;
1085
1086 /* compute original phantom points */
1087 hinter->pp1.x = 0;
1088 hinter->pp1.y = 0;
1089 hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
1090 hinter->pp2.y = 0;
1091
1092 /* be sure to check for spacing glyphs */
1093 if ( slot->outline.n_points == 0 )
1094 goto Hint_Metrics;
1095
1096 /* now, load the slot image into the auto-outline, and run the */
1097 /* automatic hinting process */
1098 error = ah_outline_load( outline, face ); /* XXX: change to slot */
1099 if ( error )
1100 goto Exit;
1101
1102 /* perform feature detection */
1103 ah_outline_detect_features( outline );
1104
1105 if ( !no_horz_hints )
1106 {
1107 ah_outline_compute_blue_edges( outline, hinter->globals );
1108 ah_outline_scale_blue_edges( outline, hinter->globals );
1109 }
1110
1111 /* perform alignment control */
1112 ah_hinter_hint_edges( hinter, no_horz_hints, no_vert_hints );
1113 ah_hinter_align( hinter );
1114
1115 /* now save the current outline into the loader's current table */
1116 ah_outline_save( outline, gloader );
1117
1118 /* we now need to hint the metrics according to the change in */
1119 /* width/positioning that occured during the hinting process */
1120 {
1121 FT_Pos old_width, new_width;
1122 FT_Pos old_advance, new_advance;
1123 FT_Pos old_lsb, new_lsb;
1124 AH_Edge* edge1 = outline->vert_edges; /* leftmost edge */
1125 AH_Edge* edge2 = edge1 +
1126 outline->num_vedges - 1; /* rightmost edge */
David Turner3469d0d2000-07-19 20:02:14 +00001127
1128
Werner Lembergc3dd1512000-07-26 14:11:15 +00001129 old_width = edge2->opos - edge1->opos;
1130 new_width = edge2->pos - edge1->pos;
1131
1132 old_advance = hinter->pp2.x;
1133 old_lsb = edge1->opos;
1134 new_lsb = edge1->pos;
1135
1136 new_advance = old_advance +
1137 ( new_width + new_lsb - old_width - old_lsb );
1138
1139 hinter->pp1.x = ( ( new_lsb - old_lsb ) + 32 ) & -64;
Werner Lemberge4b32a52000-10-31 20:42:18 +00001140 hinter->pp2.x = ( ( edge2->pos +
Werner Lembergc3dd1512000-07-26 14:11:15 +00001141 ( old_advance - edge2->opos ) ) + 32 ) & -64;
1142 }
1143
1144 /* good, we simply add the glyph to our loader's base */
1145 ah_loader_add( gloader );
1146 break;
1147
1148 case ft_glyph_format_composite:
1149 {
1150 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
1151 FT_UInt num_base_subgs, start_point, start_contour;
1152 FT_SubGlyph* subglyph;
1153
1154
1155 start_point = gloader->base.outline.n_points;
1156 start_contour = gloader->base.outline.n_contours;
1157
1158 /* first of all, copy the subglyph descriptors in the glyph loader */
1159 error = ah_loader_check_subglyphs( gloader, num_subglyphs );
1160 if ( error )
1161 goto Exit;
1162
1163 MEM_Copy( gloader->current.subglyphs, slot->subglyphs,
1164 num_subglyphs * sizeof ( FT_SubGlyph ) );
1165
1166 gloader->current.num_subglyphs = num_subglyphs;
1167 num_base_subgs = gloader->base.num_subglyphs;
1168
1169 /* now, read each subglyph independently */
1170 for ( nn = 0; nn < num_subglyphs; nn++ )
1171 {
1172 FT_Vector pp1, pp2;
1173 FT_Pos x, y;
1174 FT_UInt num_points, num_new_points, num_base_points;
1175
1176
1177 /* gloader.current.subglyphs can change during glyph loading due */
1178 /* to re-allocation -- we must recompute the current subglyph on */
1179 /* each iteration */
1180 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1181
1182 pp1 = hinter->pp1;
1183 pp2 = hinter->pp2;
1184
1185 num_base_points = gloader->base.outline.n_points;
1186
1187 error = ah_hinter_load( hinter, subglyph->index,
1188 load_flags, depth + 1 );
1189 if ( error )
1190 goto Exit;
1191
1192 /* recompute subglyph pointer */
1193 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
1194
1195 if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
1196 {
1197 pp1 = hinter->pp1;
1198 pp2 = hinter->pp2;
1199 }
1200 else
1201 {
1202 hinter->pp1 = pp1;
1203 hinter->pp2 = pp2;
1204 }
1205
1206 num_points = gloader->base.outline.n_points;
1207 num_new_points = num_points - num_base_points;
1208
1209 /* now perform the transform required for this subglyph */
1210
1211 if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE |
1212 FT_SUBGLYPH_FLAG_XY_SCALE |
1213 FT_SUBGLYPH_FLAG_2X2 ) )
1214 {
1215 FT_Vector* cur = gloader->base.outline.points +
1216 num_base_points;
1217 FT_Vector* org = gloader->base.extra_points +
1218 num_base_points;
1219 FT_Vector* limit = cur + num_new_points;
1220
1221
1222 for ( ; cur < limit; cur++, org++ )
1223 {
1224 FT_Vector_Transform( cur, &subglyph->transform );
1225 FT_Vector_Transform( org, &subglyph->transform );
1226 }
1227 }
1228
1229 /* apply offset */
1230
1231 if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
1232 {
1233 FT_Int k = subglyph->arg1;
1234 FT_UInt l = subglyph->arg2;
1235 FT_Vector* p1;
1236 FT_Vector* p2;
1237
1238
1239 if ( start_point + k >= num_base_points ||
1240 l >= (FT_UInt)num_new_points )
1241 {
1242 error = FT_Err_Invalid_Composite;
1243 goto Exit;
1244 }
1245
1246 l += num_base_points;
1247
1248 /* for now, only use the current point coordinates */
1249 /* we may consider another approach in the near future */
1250 p1 = gloader->base.outline.points + start_point + k;
1251 p2 = gloader->base.outline.points + start_point + l;
1252
1253 x = p1->x - p2->x;
1254 y = p1->y - p2->y;
1255 }
1256 else
1257 {
1258 x = FT_MulFix( subglyph->arg1, x_scale );
1259 y = FT_MulFix( subglyph->arg2, y_scale );
1260
1261 x = ( x + 32 ) & -64;
1262 y = ( y + 32 ) & -64;
1263 }
1264
1265 {
1266 FT_Outline dummy = gloader->base.outline;
1267
1268
1269 dummy.points += num_base_points;
1270 dummy.n_points = num_new_points;
1271
1272 FT_Outline_Translate( &dummy, x, y );
1273 }
1274 }
1275 }
1276 break;
1277
1278 default:
1279 /* we don't support other formats (yet?) */
1280 error = FT_Err_Unimplemented_Feature;
1281 }
1282
1283 Hint_Metrics:
1284 if ( depth == 0 )
1285 {
1286 FT_BBox bbox;
1287
Werner Lemberg5c4f5eb2000-11-18 04:42:41 +00001288
David Turnere0066752000-11-16 19:06:28 +00001289 /* transform the hinted outline if needed */
1290 if ( hinter->transformed )
1291 FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix );
Werner Lembergc3dd1512000-07-26 14:11:15 +00001292
1293 /* we must translate our final outline by -pp1.x, and compute */
1294 /* the new metrics */
1295 if ( hinter->pp1.x )
1296 FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
1297
1298 FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
1299 bbox.xMin &= -64;
1300 bbox.yMin &= -64;
1301 bbox.xMax = ( bbox.xMax + 63 ) & -64;
1302 bbox.yMax = ( bbox.yMax + 63 ) & -64;
1303
1304 slot->metrics.width = bbox.xMax - bbox.xMin;
1305 slot->metrics.height = bbox.yMax - bbox.yMin;
1306 slot->metrics.horiBearingX = bbox.xMin;
1307 slot->metrics.horiBearingY = bbox.yMax;
1308 slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x;
1309 /* XXX: TO DO - slot->linearHoriAdvance */
1310
1311 /* now copy outline into glyph slot */
David Turner54e75742000-11-04 02:52:02 +00001312 ah_loader_rewind( slot->internal->loader );
1313 error = ah_loader_copy_points( slot->internal->loader, gloader );
Werner Lembergc3dd1512000-07-26 14:11:15 +00001314 if ( error )
1315 goto Exit;
1316
David Turner54e75742000-11-04 02:52:02 +00001317 slot->outline = slot->internal->loader->base.outline;
Werner Lembergc3dd1512000-07-26 14:11:15 +00001318 slot->format = ft_glyph_format_outline;
1319 }
1320
1321 Exit:
1322 return error;
1323 }
1324
1325
1326 /* load and hint a given glyph */
David Turner76a5f622000-11-04 01:55:49 +00001327 FT_LOCAL_DEF
Werner Lembergc3dd1512000-07-26 14:11:15 +00001328 FT_Error ah_hinter_load_glyph( AH_Hinter* hinter,
1329 FT_GlyphSlot slot,
1330 FT_Size size,
1331 FT_UInt glyph_index,
1332 FT_Int load_flags )
1333 {
1334 FT_Face face = slot->face;
1335 FT_Error error;
1336 FT_Fixed x_scale = size->metrics.x_scale;
1337 FT_Fixed y_scale = size->metrics.y_scale;
1338 AH_Face_Globals* face_globals = FACE_GLOBALS( face );
1339
Werner Lemberg5c4f5eb2000-11-18 04:42:41 +00001340
Werner Lembergc3dd1512000-07-26 14:11:15 +00001341 /* first of all, we need to check that we're using the correct face and */
1342 /* global hints to load the glyph */
1343 if ( hinter->face != face || hinter->globals != face_globals )
1344 {
1345 hinter->face = face;
1346 if ( !face_globals )
1347 {
1348 error = ah_hinter_new_face_globals( hinter, face, 0 );
1349 if ( error )
1350 goto Exit;
1351 }
1352 hinter->globals = FACE_GLOBALS( face );
1353 face_globals = FACE_GLOBALS( face );
1354 }
1355
1356 /* now, we must check the current character pixel size to see if we */
1357 /* need to rescale the global metrics */
1358 if ( face_globals->x_scale != x_scale ||
1359 face_globals->y_scale != y_scale )
1360 ah_hinter_scale_globals( hinter, x_scale, y_scale );
1361
1362 load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE;
1363
1364 ah_loader_rewind( hinter->loader );
1365
David Turnere0066752000-11-16 19:06:28 +00001366 {
1367 FT_Slot_Internal internal = slot->internal;
1368
Werner Lemberg5c4f5eb2000-11-18 04:42:41 +00001369
David Turnere0066752000-11-16 19:06:28 +00001370 hinter->transformed = internal->glyph_transformed;
Werner Lemberg5c4f5eb2000-11-18 04:42:41 +00001371 if ( hinter->transformed )
David Turnere0066752000-11-16 19:06:28 +00001372 {
1373 FT_Matrix imatrix;
1374
Werner Lemberg5c4f5eb2000-11-18 04:42:41 +00001375
David Turnere0066752000-11-16 19:06:28 +00001376 imatrix = internal->glyph_matrix;
1377 hinter->trans_delta = internal->glyph_delta;
1378 hinter->trans_matrix = imatrix;
1379
1380 FT_Matrix_Invert( &imatrix );
1381 FT_Vector_Transform( &hinter->trans_delta, &imatrix );
1382 }
1383 }
1384
Werner Lembergc3dd1512000-07-26 14:11:15 +00001385 error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
1386
1387 Exit:
1388 return error;
1389 }
1390
1391
1392 /* retrieve a face's autohint globals for client applications */
David Turner76a5f622000-11-04 01:55:49 +00001393 FT_LOCAL_DEF
David Turner3469d0d2000-07-19 20:02:14 +00001394 void ah_hinter_get_global_hints( AH_Hinter* hinter,
1395 FT_Face face,
Werner Lembergc3dd1512000-07-26 14:11:15 +00001396 void** global_hints,
1397 long* global_len )
David Turner3469d0d2000-07-19 20:02:14 +00001398 {
1399 AH_Globals* globals = 0;
1400 FT_Memory memory = hinter->memory;
1401 FT_Error error;
1402
Werner Lembergc3dd1512000-07-26 14:11:15 +00001403
David Turner3469d0d2000-07-19 20:02:14 +00001404 /* allocate new master globals */
Werner Lembergc3dd1512000-07-26 14:11:15 +00001405 if ( ALLOC( globals, sizeof ( *globals ) ) )
David Turner3469d0d2000-07-19 20:02:14 +00001406 goto Fail;
1407
1408 /* compute face globals if needed */
Werner Lembergc3dd1512000-07-26 14:11:15 +00001409 if ( !FACE_GLOBALS( face ) )
David Turner3469d0d2000-07-19 20:02:14 +00001410 {
1411 error = ah_hinter_new_face_globals( hinter, face, 0 );
Werner Lembergc3dd1512000-07-26 14:11:15 +00001412 if ( error )
1413 goto Fail;
David Turner3469d0d2000-07-19 20:02:14 +00001414 }
1415
Werner Lembergc3dd1512000-07-26 14:11:15 +00001416 *globals = FACE_GLOBALS( face )->design;
David Turner3469d0d2000-07-19 20:02:14 +00001417 *global_hints = globals;
Werner Lembergc3dd1512000-07-26 14:11:15 +00001418 *global_len = sizeof( *globals );
1419
David Turner3469d0d2000-07-19 20:02:14 +00001420 return;
1421
1422 Fail:
1423 FREE( globals );
Werner Lembergc3dd1512000-07-26 14:11:15 +00001424
David Turner3469d0d2000-07-19 20:02:14 +00001425 *global_hints = 0;
1426 *global_len = 0;
1427 }
1428
1429
David Turner76a5f622000-11-04 01:55:49 +00001430 FT_LOCAL_DEF
David Turner3469d0d2000-07-19 20:02:14 +00001431 void ah_hinter_done_global_hints( AH_Hinter* hinter,
1432 void* global_hints )
1433 {
Werner Lembergc3dd1512000-07-26 14:11:15 +00001434 FT_Memory memory = hinter->memory;
1435
1436
David Turner3469d0d2000-07-19 20:02:14 +00001437 FREE( global_hints );
1438 }
1439
1440
Werner Lembergc3dd1512000-07-26 14:11:15 +00001441/* END */