]>
Commit | Line | Data |
---|---|---|
cabec872 RR |
1 | /***************************************************************************/ |
2 | /* */ | |
3 | /* ahhint.c */ | |
4 | /* */ | |
5 | /* Glyph hinter (body). */ | |
6 | /* */ | |
7 | /* Copyright 2000 Catharon Productions Inc. */ | |
8 | /* 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 */ | |
13 | /* `CatharonLicense.txt'. By continuing to use, modify, or distribute */ | |
14 | /* this file you indicate that you have read the license and */ | |
15 | /* understand and accept it fully. */ | |
16 | /* */ | |
17 | /* Note that this license is compatible with the FreeType license. */ | |
18 | /* */ | |
19 | /***************************************************************************/ | |
20 | ||
21 | ||
22 | #ifdef FT_FLAT_COMPILE | |
23 | ||
24 | #include "ahhint.h" | |
25 | #include "ahglyph.h" | |
26 | #include "ahangles.h" | |
27 | ||
28 | #else | |
29 | ||
30 | #include <autohint/ahhint.h> | |
31 | #include <autohint/ahglyph.h> | |
32 | #include <autohint/ahangles.h> | |
33 | ||
34 | #endif | |
35 | ||
36 | #include <freetype/ftoutln.h> | |
37 | ||
38 | ||
39 | #define FACE_GLOBALS( face ) ((AH_Face_Globals*)(face)->autohint.data) | |
40 | ||
41 | #define AH_USE_IUP | |
42 | ||
43 | ||
44 | /*************************************************************************/ | |
45 | /*************************************************************************/ | |
46 | /**** ****/ | |
47 | /**** Hinting routines ****/ | |
48 | /**** ****/ | |
49 | /*************************************************************************/ | |
50 | /*************************************************************************/ | |
51 | ||
52 | ||
53 | static int disable_horz_edges = 0; | |
54 | static int disable_vert_edges = 0; | |
55 | ||
56 | ||
57 | /* snap a given width in scaled coordinates to one of the */ | |
58 | /* current standard widths */ | |
59 | static | |
60 | FT_Pos ah_snap_width( FT_Pos* widths, | |
61 | FT_Int count, | |
62 | FT_Pos width ) | |
63 | { | |
64 | int n; | |
65 | FT_Pos best = 64 + 32 + 2; | |
66 | FT_Pos reference = width; | |
67 | ||
68 | ||
69 | for ( n = 0; n < count; n++ ) | |
70 | { | |
71 | FT_Pos w; | |
72 | FT_Pos dist; | |
73 | ||
74 | ||
75 | w = widths[n]; | |
76 | dist = width - w; | |
77 | if ( dist < 0 ) | |
78 | dist = -dist; | |
79 | if ( dist < best ) | |
80 | { | |
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 | ||
103 | /* align one stem edge relative to the previous stem edge */ | |
104 | 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 | ||
114 | ||
115 | if ( dist < 0 ) | |
116 | { | |
117 | dist = -dist; | |
118 | sign = -1; | |
119 | } | |
120 | ||
121 | if ( vertical ) | |
122 | { | |
123 | dist = ah_snap_width( globals->heights, globals->num_heights, dist ); | |
124 | ||
125 | /* in the case of vertical hinting, always round */ | |
126 | /* the stem heights to integer pixels */ | |
127 | if ( dist >= 64 ) | |
128 | dist = ( dist + 16 ) & -64; | |
129 | else | |
130 | dist = 64; | |
131 | } | |
132 | else | |
133 | { | |
134 | dist = ah_snap_width( globals->widths, globals->num_widths, dist ); | |
135 | ||
136 | if ( hinter->flags & ah_hinter_monochrome ) | |
137 | { | |
138 | /* monochrome horizontal hinting: snap widths to integer pixels */ | |
139 | /* with a different threshold */ | |
140 | if ( dist < 64 ) | |
141 | dist = 64; | |
142 | else | |
143 | dist = ( dist + 32 ) & -64; | |
144 | } | |
145 | else | |
146 | { | |
147 | /* for horizontal anti-aliased hinting, we adopt a more subtle */ | |
148 | /* approach: we strengthen small stems, round stems whose size */ | |
149 | /* is between 1 and 2 pixels to an integer, otherwise nothing */ | |
150 | if ( dist < 48 ) | |
151 | dist = ( dist + 64 ) >> 1; | |
152 | ||
153 | else if ( dist < 128 ) | |
154 | dist = ( dist + 42 ) & -64; | |
155 | } | |
156 | } | |
157 | ||
158 | stem_edge->pos = base_edge->pos + sign * dist; | |
159 | } | |
160 | ||
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 | ||
170 | UNUSED( hinter ); | |
171 | ||
172 | ||
173 | dist = serif->opos - base->opos; | |
174 | if ( dist < 0 ) | |
175 | { | |
176 | dist = -dist; | |
177 | sign = -1; | |
178 | } | |
179 | ||
180 | /* do not strengthen serifs */ | |
181 | if ( base->flags & ah_edge_done ) | |
182 | { | |
183 | if ( dist > 64 ) | |
184 | dist = ( dist + 16 ) & -64; | |
185 | ||
186 | else if ( dist <= 32 ) | |
187 | dist = ( dist + 33 ) >> 1; | |
188 | } | |
189 | ||
190 | serif->pos = base->pos + sign * dist; | |
191 | } | |
192 | ||
193 | ||
194 | /*************************************************************************/ | |
195 | /*************************************************************************/ | |
196 | /*************************************************************************/ | |
197 | /**** ****/ | |
198 | /**** E D G E H I N T I N G ****/ | |
199 | /**** ****/ | |
200 | /*************************************************************************/ | |
201 | /*************************************************************************/ | |
202 | /*************************************************************************/ | |
203 | ||
204 | ||
205 | /* Another alternative edge hinting algorithm */ | |
206 | static | |
207 | void ah_hint_edges_3( AH_Hinter* hinter ) | |
208 | { | |
209 | AH_Edge* edges; | |
210 | AH_Edge* edge_limit; | |
211 | AH_Outline* outline = hinter->glyph; | |
212 | FT_Int dimension; | |
213 | ||
214 | ||
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 | ||
226 | ||
227 | if ( disable_vert_edges && !dimension ) | |
228 | goto Next_Dimension; | |
229 | ||
230 | if ( disable_horz_edges && dimension ) | |
231 | goto Next_Dimension; | |
232 | ||
233 | /* we begin by aligning all stems relative to the blue zone */ | |
234 | /* if needed -- that's only for horizontal edges */ | |
235 | if ( dimension ) | |
236 | { | |
237 | for ( edge = edges; edge < edge_limit; edge++ ) | |
238 | { | |
239 | FT_Pos* blue; | |
240 | AH_Edge *edge1, *edge2; | |
241 | ||
242 | ||
243 | if ( edge->flags & ah_edge_done ) | |
244 | continue; | |
245 | ||
246 | blue = edge->blue_edge; | |
247 | edge1 = 0; | |
248 | edge2 = edge->link; | |
249 | ||
250 | if ( blue ) | |
251 | { | |
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 | ||
261 | if ( !edge1 ) | |
262 | continue; | |
263 | ||
264 | edge1->pos = blue[0]; | |
265 | edge1->flags |= ah_edge_done; | |
266 | ||
267 | if ( edge2 && !edge2->blue_edge ) | |
268 | { | |
269 | ah_align_linked_edge( hinter, edge1, edge2, dimension ); | |
270 | edge2->flags |= ah_edge_done; | |
271 | } | |
272 | ||
273 | if ( !anchor ) | |
274 | 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 | ||
286 | ||
287 | if ( edge->flags & ah_edge_done ) | |
288 | continue; | |
289 | ||
290 | /* skip all non-stem edges */ | |
291 | edge2 = edge->link; | |
292 | if ( !edge2 ) | |
293 | { | |
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.. */ | |
301 | if ( edge2->blue_edge || edge2 < edge ) | |
302 | { | |
303 | ||
304 | #if 0 | |
305 | printf( "strange blue alignement, edge %d to %d\n", | |
306 | edge - edges, edge2 - edges ); | |
307 | #endif | |
308 | ||
309 | 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 | ||
318 | if ( !anchor ) | |
319 | { | |
320 | edge->pos = ( edge->opos + 32 ) & -64; | |
321 | anchor = edge; | |
322 | } | |
323 | else | |
324 | edge->pos = anchor->pos + | |
325 | ( ( edge->opos - anchor->opos + 32 ) & -64 ); | |
326 | ||
327 | edge->flags |= ah_edge_done; | |
328 | ||
329 | if ( edge > edges && edge->pos < edge[-1].pos ) | |
330 | { | |
331 | edge->pos = edge[-1].pos; | |
332 | min = 1; | |
333 | } | |
334 | ||
335 | ah_align_linked_edge( hinter, edge, edge2, dimension ); | |
336 | delta = 0; | |
337 | if ( edge2 + 1 < edge_limit && | |
338 | edge2[1].flags & ah_edge_done ) | |
339 | delta = edge2[1].pos - edge2->pos; | |
340 | ||
341 | if ( delta < 0 ) | |
342 | { | |
343 | edge2->pos += delta; | |
344 | if ( !min ) | |
345 | edge->pos += delta; | |
346 | } | |
347 | edge2->flags |= ah_edge_done; | |
348 | } | |
349 | } | |
350 | ||
351 | if ( !has_serifs ) | |
352 | goto Next_Dimension; | |
353 | ||
354 | /* now, hint the remaining edges (serifs and single) in order */ | |
355 | /* to complete our processing */ | |
356 | for ( edge = edges; edge < edge_limit; edge++ ) | |
357 | { | |
358 | if ( edge->flags & ah_edge_done ) | |
359 | continue; | |
360 | ||
361 | if ( edge->serif ) | |
362 | { | |
363 | ah_align_serif_edge( hinter, edge->serif, edge ); | |
364 | } | |
365 | else if ( !anchor ) | |
366 | { | |
367 | edge->pos = ( edge->opos + 32 ) & -64; | |
368 | anchor = edge; | |
369 | } | |
370 | else | |
371 | edge->pos = anchor->pos + | |
372 | ( ( edge->opos-anchor->opos + 32 ) & -64 ); | |
373 | ||
374 | edge->flags |= ah_edge_done; | |
375 | ||
376 | if ( edge > edges && edge->pos < edge[-1].pos ) | |
377 | edge->pos = edge[-1].pos; | |
378 | ||
379 | if ( edge + 1 < edge_limit && | |
380 | edge[1].flags & ah_edge_done && | |
381 | edge->pos > edge[1].pos ) | |
382 | edge->pos = edge[1].pos; | |
383 | } | |
384 | ||
385 | Next_Dimension: | |
386 | edges = outline->vert_edges; | |
387 | edge_limit = edges + outline->num_vedges; | |
388 | } | |
389 | } | |
390 | ||
391 | ||
392 | LOCAL_FUNC | |
393 | 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 | ||
400 | /* 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? */ | |
403 | { | |
404 | ah_hint_edges_3( hinter ); | |
405 | ||
406 | #if 0 | |
407 | /* outline optimizer removed temporarily */ | |
408 | if ( hinter->flags & ah_hinter_optimize ) | |
409 | { | |
410 | AH_Optimizer opt; | |
411 | ||
412 | ||
413 | if ( !AH_Optimizer_Init( &opt, hinter->glyph, hinter->memory ) ) | |
414 | { | |
415 | AH_Optimizer_Compute( &opt ); | |
416 | AH_Optimizer_Done( &opt ); | |
417 | } | |
418 | } | |
419 | #endif | |
420 | ||
421 | } | |
422 | } | |
423 | ||
424 | ||
425 | /*************************************************************************/ | |
426 | /*************************************************************************/ | |
427 | /*************************************************************************/ | |
428 | /**** ****/ | |
429 | /**** P O I N T H I N T I N G ****/ | |
430 | /**** ****/ | |
431 | /*************************************************************************/ | |
432 | /*************************************************************************/ | |
433 | /*************************************************************************/ | |
434 | ||
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 | ||
443 | ||
444 | edges = outline->horz_edges; | |
445 | edge_limit = edges + outline->num_hedges; | |
446 | ||
447 | for ( dimension = 1; dimension >= 0; dimension-- ) | |
448 | { | |
449 | AH_Edge* edge; | |
450 | AH_Edge* before; | |
451 | AH_Edge* after; | |
452 | ||
453 | ||
454 | before = 0; | |
455 | after = 0; | |
456 | ||
457 | edge = edges; | |
458 | for ( ; edge < edge_limit; edge++ ) | |
459 | { | |
460 | /* move the points of each segment */ | |
461 | /* in each edge to the edge's position */ | |
462 | AH_Segment* seg = edge->first; | |
463 | ||
464 | ||
465 | do | |
466 | { | |
467 | AH_Point* point = seg->first; | |
468 | ||
469 | ||
470 | for (;;) | |
471 | { | |
472 | if ( dimension ) | |
473 | { | |
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 | } | |
482 | ||
483 | if ( point == seg->last ) | |
484 | break; | |
485 | ||
486 | point = point->next; | |
487 | } | |
488 | ||
489 | seg = seg->edge_next; | |
490 | ||
491 | } while ( seg != edge->first ); | |
492 | } | |
493 | ||
494 | edges = outline->vert_edges; | |
495 | edge_limit = edges + outline->num_vedges; | |
496 | } | |
497 | } | |
498 | ||
499 | ||
500 | /* hint the strong points -- this is equivalent to the TrueType `IP' */ | |
501 | 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 | ||
512 | ||
513 | 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; | |
524 | AH_Edge* before; | |
525 | AH_Edge* after; | |
526 | ||
527 | ||
528 | before = 0; | |
529 | after = 0; | |
530 | ||
531 | if ( edges < edge_limit ) | |
532 | for ( point = points; point < point_limit; point++ ) | |
533 | { | |
534 | FT_Pos u, ou, fu; /* point position */ | |
535 | FT_Pos delta; | |
536 | ||
537 | ||
538 | if ( point->flags & touch_flag ) | |
539 | continue; | |
540 | ||
541 | #ifndef AH_OPTION_NO_WEAK_INTERPOLATION | |
542 | /* if this point is candidate to weak interpolation, we will */ | |
543 | /* interpolate it after all strong points have been processed */ | |
544 | if ( point->flags & ah_flah_weak_interpolation ) | |
545 | continue; | |
546 | #endif | |
547 | ||
548 | if ( dimension ) | |
549 | { | |
550 | u = point->fy; | |
551 | ou = point->oy; | |
552 | } | |
553 | else | |
554 | { | |
555 | u = point->fx; | |
556 | ou = point->ox; | |
557 | } | |
558 | ||
559 | fu = u; | |
560 | ||
561 | /* is the point before the first edge? */ | |
562 | edge = edges; | |
563 | delta = edge->fpos - u; | |
564 | if ( delta >= 0 ) | |
565 | { | |
566 | u = edge->pos - ( edge->opos - ou ); | |
567 | goto Store_Point; | |
568 | } | |
569 | ||
570 | /* is the point after the last edge ? */ | |
571 | edge = edge_limit - 1; | |
572 | delta = u - edge->fpos; | |
573 | if ( delta >= 0 ) | |
574 | { | |
575 | u = edge->pos + ( ou - edge->opos ); | |
576 | goto Store_Point; | |
577 | } | |
578 | ||
579 | /* otherwise, interpolate the point in between */ | |
580 | { | |
581 | AH_Edge* before = 0; | |
582 | AH_Edge* after = 0; | |
583 | ||
584 | ||
585 | for ( edge = edges; edge < edge_limit; edge++ ) | |
586 | { | |
587 | if ( u == edge->fpos ) | |
588 | { | |
589 | u = edge->pos; | |
590 | goto Store_Point; | |
591 | } | |
592 | if ( u < edge->fpos ) | |
593 | break; | |
594 | before = edge; | |
595 | } | |
596 | ||
597 | for ( edge = edge_limit - 1; edge >= edges; edge-- ) | |
598 | { | |
599 | if ( u == edge->fpos ) | |
600 | { | |
601 | u = edge->pos; | |
602 | goto Store_Point; | |
603 | } | |
604 | if ( u > edge->fpos ) | |
605 | break; | |
606 | after = edge; | |
607 | } | |
608 | ||
609 | /* assert( before && after && before != after ) */ | |
610 | u = before->pos + FT_MulDiv( fu - before->fpos, | |
611 | after->pos - before->pos, | |
612 | after->fpos - before->fpos ); | |
613 | } | |
614 | ||
615 | Store_Point: | |
616 | ||
617 | /* save the point position */ | |
618 | if ( dimension ) | |
619 | point->y = u; | |
620 | else | |
621 | point->x = u; | |
622 | ||
623 | point->flags |= touch_flag; | |
624 | } | |
625 | ||
626 | edges = outline->vert_edges; | |
627 | edge_limit = edges + outline->num_vedges; | |
628 | touch_flag = ah_flah_touch_x; | |
629 | } | |
630 | } | |
631 | ||
632 | ||
633 | #ifndef AH_OPTION_NO_WEAK_INTERPOLATION | |
634 | ||
635 | static | |
636 | void ah_iup_shift( AH_Point* p1, | |
637 | AH_Point* p2, | |
638 | AH_Point* ref ) | |
639 | { | |
640 | AH_Point* p; | |
641 | FT_Pos delta = ref->u - ref->v; | |
642 | ||
643 | ||
644 | for ( p = p1; p < ref; p++ ) | |
645 | p->u = p->v + delta; | |
646 | ||
647 | for ( p = ref + 1; p <= p2; p++ ) | |
648 | p->u = p->v + delta; | |
649 | } | |
650 | ||
651 | ||
652 | static | |
653 | void ah_iup_interp( AH_Point* p1, | |
654 | AH_Point* p2, | |
655 | AH_Point* ref1, | |
656 | AH_Point* ref2 ) | |
657 | { | |
658 | AH_Point* p; | |
659 | FT_Pos u; | |
660 | FT_Pos v1 = ref1->v; | |
661 | FT_Pos v2 = ref2->v; | |
662 | FT_Pos d1 = ref1->u - v1; | |
663 | FT_Pos d2 = ref2->u - v2; | |
664 | ||
665 | ||
666 | if ( p1 > p2 ) | |
667 | return; | |
668 | ||
669 | if ( v1 == v2 ) | |
670 | { | |
671 | for ( p = p1; p <= p2; p++ ) | |
672 | { | |
673 | FT_Pos u = p->v; | |
674 | ||
675 | ||
676 | if ( u <= v1 ) | |
677 | u += d1; | |
678 | else | |
679 | u += d2; | |
680 | ||
681 | p->u = u; | |
682 | } | |
683 | return; | |
684 | } | |
685 | ||
686 | if ( v1 < v2 ) | |
687 | { | |
688 | for ( p = p1; p <= p2; p++ ) | |
689 | { | |
690 | u = p->v; | |
691 | ||
692 | if ( u <= v1 ) | |
693 | u += d1; | |
694 | else if ( u >= v2 ) | |
695 | u += d2; | |
696 | else | |
697 | u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 ); | |
698 | ||
699 | p->u = u; | |
700 | } | |
701 | } | |
702 | else | |
703 | { | |
704 | for ( p = p1; p <= p2; p++ ) | |
705 | { | |
706 | u = p->v; | |
707 | ||
708 | if ( u <= v2 ) | |
709 | u += d2; | |
710 | else if ( u >= v1 ) | |
711 | u += d1; | |
712 | else | |
713 | u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 ); | |
714 | ||
715 | p->u = u; | |
716 | } | |
717 | } | |
718 | } | |
719 | ||
720 | ||
721 | /* interpolate weak points -- this is equivalent to the TrueType `IUP' */ | |
722 | static | |
723 | void ah_hinter_align_weak_points( AH_Hinter* hinter ) | |
724 | { | |
725 | AH_Outline* outline = hinter->glyph; | |
726 | FT_Int dimension; | |
727 | AH_Edge* edges; | |
728 | AH_Edge* edge_limit; | |
729 | AH_Point* points; | |
730 | AH_Point* point_limit; | |
731 | AH_Point** contour_limit; | |
732 | AH_Flags touch_flag; | |
733 | ||
734 | ||
735 | points = outline->points; | |
736 | point_limit = points + outline->num_points; | |
737 | ||
738 | /* PASS 1: Move segment points to edge positions */ | |
739 | ||
740 | edges = outline->horz_edges; | |
741 | edge_limit = edges + outline->num_hedges; | |
742 | touch_flag = ah_flah_touch_y; | |
743 | ||
744 | contour_limit = outline->contours + outline->num_contours; | |
745 | ||
746 | ah_setup_uv( outline, ah_uv_oy ); | |
747 | ||
748 | for ( dimension = 1; dimension >= 0; dimension-- ) | |
749 | { | |
750 | AH_Point* point; | |
751 | AH_Point* end_point; | |
752 | AH_Point* first_point; | |
753 | AH_Point** contour; | |
754 | ||
755 | ||
756 | point = points; | |
757 | contour = outline->contours; | |
758 | ||
759 | for ( ; contour < contour_limit; contour++ ) | |
760 | { | |
761 | point = *contour; | |
762 | end_point = point->prev; | |
763 | first_point = point; | |
764 | ||
765 | while ( point <= end_point && !( point->flags & touch_flag ) ) | |
766 | point++; | |
767 | ||
768 | if ( point <= end_point ) | |
769 | { | |
770 | AH_Point* first_touched = point; | |
771 | AH_Point* cur_touched = point; | |
772 | ||
773 | ||
774 | point++; | |
775 | while ( point <= end_point ) | |
776 | { | |
777 | if ( point->flags & touch_flag ) | |
778 | { | |
779 | /* we found two successive touched points; we interpolate */ | |
780 | /* all contour points between them */ | |
781 | ah_iup_interp( cur_touched + 1, point - 1, | |
782 | cur_touched, point ); | |
783 | cur_touched = point; | |
784 | } | |
785 | point++; | |
786 | } | |
787 | ||
788 | if ( cur_touched == first_touched ) | |
789 | { | |
790 | /* this is a special case: only one point was touched in the */ | |
791 | /* contour; we thus simply shift the whole contour */ | |
792 | ah_iup_shift( first_point, end_point, cur_touched ); | |
793 | } | |
794 | else | |
795 | { | |
796 | /* now interpolate after the last touched point to the end */ | |
797 | /* of the contour */ | |
798 | ah_iup_interp( cur_touched + 1, end_point, | |
799 | cur_touched, first_touched ); | |
800 | ||
801 | /* if the first contour point isn't touched, interpolate */ | |
802 | /* from the contour start to the first touched point */ | |
803 | if ( first_touched > points ) | |
804 | ah_iup_interp( first_point, first_touched - 1, | |
805 | cur_touched, first_touched ); | |
806 | } | |
807 | } | |
808 | } | |
809 | ||
810 | /* now save the interpolated values back to x/y */ | |
811 | if ( dimension ) | |
812 | { | |
813 | for ( point = points; point < point_limit; point++ ) | |
814 | point->y = point->u; | |
815 | ||
816 | touch_flag = ah_flah_touch_x; | |
817 | ah_setup_uv( outline, ah_uv_ox ); | |
818 | } | |
819 | else | |
820 | { | |
821 | for ( point = points; point < point_limit; point++ ) | |
822 | point->x = point->u; | |
823 | ||
824 | break; /* exit loop */ | |
825 | } | |
826 | } | |
827 | } | |
828 | ||
829 | #endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */ | |
830 | ||
831 | ||
832 | LOCAL_FUNC | |
833 | void ah_hinter_align_points( AH_Hinter* hinter ) | |
834 | { | |
835 | ah_hinter_align_edge_points( hinter ); | |
836 | ||
837 | #ifndef AH_OPTION_NO_STRONG_INTERPOLATION | |
838 | ah_hinter_align_strong_points( hinter ); | |
839 | #endif | |
840 | ||
841 | #ifndef AH_OPTION_NO_WEAK_INTERPOLATION | |
842 | ah_hinter_align_weak_points( hinter ); | |
843 | #endif | |
844 | } | |
845 | ||
846 | ||
847 | /*************************************************************************/ | |
848 | /*************************************************************************/ | |
849 | /*************************************************************************/ | |
850 | /**** ****/ | |
851 | /**** H I N T E R O B J E C T M E T H O D S ****/ | |
852 | /**** ****/ | |
853 | /*************************************************************************/ | |
854 | /*************************************************************************/ | |
855 | /*************************************************************************/ | |
856 | ||
857 | ||
858 | /* scale and fit the global metrics */ | |
859 | static | |
860 | void ah_hinter_scale_globals( AH_Hinter* hinter, | |
861 | FT_Fixed x_scale, | |
862 | FT_Fixed y_scale ) | |
863 | { | |
864 | FT_Int n; | |
865 | AH_Face_Globals* globals = hinter->globals; | |
866 | AH_Globals* design = &globals->design; | |
867 | AH_Globals* scaled = &globals->scaled; | |
868 | ||
869 | ||
870 | /* copy content */ | |
871 | *scaled = *design; | |
872 | ||
873 | /* scale the standard widths & heights */ | |
874 | for ( n = 0; n < design->num_widths; n++ ) | |
875 | scaled->widths[n] = FT_MulFix( design->widths[n], x_scale ); | |
876 | ||
877 | for ( n = 0; n < design->num_heights; n++ ) | |
878 | scaled->heights[n] = FT_MulFix( design->heights[n], y_scale ); | |
879 | ||
880 | /* scale the blue zones */ | |
881 | for ( n = 0; n < ah_blue_max; n++ ) | |
882 | { | |
883 | FT_Pos delta, delta2; | |
884 | ||
885 | ||
886 | delta = design->blue_shoots[n] - design->blue_refs[n]; | |
887 | delta2 = delta; | |
888 | if ( delta < 0 ) | |
889 | delta2 = -delta2; | |
890 | delta2 = FT_MulFix( delta2, y_scale ); | |
891 | ||
892 | if ( delta2 < 32 ) | |
893 | delta2 = 0; | |
894 | else if ( delta2 < 64 ) | |
895 | delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & -32 ); | |
896 | else | |
897 | delta2 = ( delta2 + 32 ) & -64; | |
898 | ||
899 | if ( delta < 0 ) | |
900 | delta2 = -delta2; | |
901 | ||
902 | scaled->blue_refs[n] = | |
903 | ( FT_MulFix( design->blue_refs[n], y_scale ) + 32 ) & -64; | |
904 | scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2; | |
905 | } | |
906 | ||
907 | globals->x_scale = x_scale; | |
908 | globals->y_scale = y_scale; | |
909 | } | |
910 | ||
911 | ||
912 | static | |
913 | void ah_hinter_align( AH_Hinter* hinter ) | |
914 | { | |
915 | ah_hinter_align_edge_points( hinter ); | |
916 | ah_hinter_align_points( hinter ); | |
917 | } | |
918 | ||
919 | ||
920 | /* finalize a hinter object */ | |
921 | void ah_hinter_done( AH_Hinter* hinter ) | |
922 | { | |
923 | if ( hinter ) | |
924 | { | |
925 | FT_Memory memory = hinter->memory; | |
926 | ||
927 | ||
928 | ah_loader_done( hinter->loader ); | |
929 | ah_outline_done( hinter->glyph ); | |
930 | ||
931 | /* note: the `globals' pointer is _not_ owned by the hinter */ | |
932 | /* but by the current face object, we don't need to */ | |
933 | /* release it */ | |
934 | hinter->globals = 0; | |
935 | hinter->face = 0; | |
936 | ||
937 | FREE( hinter ); | |
938 | } | |
939 | } | |
940 | ||
941 | ||
942 | /* create a new empty hinter object */ | |
943 | FT_Error ah_hinter_new( FT_Library library, | |
944 | AH_Hinter** ahinter ) | |
945 | { | |
946 | AH_Hinter* hinter = 0; | |
947 | FT_Memory memory = library->memory; | |
948 | FT_Error error; | |
949 | ||
950 | ||
951 | *ahinter = 0; | |
952 | ||
953 | /* allocate object */ | |
954 | if ( ALLOC( hinter, sizeof ( *hinter ) ) ) | |
955 | goto Exit; | |
956 | ||
957 | hinter->memory = memory; | |
958 | hinter->flags = 0; | |
959 | ||
960 | /* allocate outline and loader */ | |
961 | error = ah_outline_new( memory, &hinter->glyph ) || | |
962 | ah_loader_new ( memory, &hinter->loader ) || | |
963 | ah_loader_create_extra( hinter->loader ); | |
964 | if ( error ) | |
965 | goto Exit; | |
966 | ||
967 | *ahinter = hinter; | |
968 | ||
969 | Exit: | |
970 | if ( error ) | |
971 | ah_hinter_done( hinter ); | |
972 | ||
973 | return error; | |
974 | } | |
975 | ||
976 | ||
977 | /* create a face's autohint globals */ | |
978 | FT_Error ah_hinter_new_face_globals( AH_Hinter* hinter, | |
979 | FT_Face face, | |
980 | AH_Globals* globals ) | |
981 | { | |
982 | FT_Error error; | |
983 | FT_Memory memory = hinter->memory; | |
984 | AH_Face_Globals* face_globals; | |
985 | ||
986 | ||
987 | if ( ALLOC( face_globals, sizeof ( *face_globals ) ) ) | |
988 | goto Exit; | |
989 | ||
990 | hinter->face = face; | |
991 | hinter->globals = face_globals; | |
992 | ||
993 | if ( globals ) | |
994 | face_globals->design = *globals; | |
995 | else | |
996 | ah_hinter_compute_globals( hinter ); | |
997 | ||
998 | face->autohint.data = face_globals; | |
999 | face->autohint.finalizer = (FT_Generic_Finalizer) | |
1000 | ah_hinter_done_face_globals; | |
1001 | face_globals->face = face; | |
1002 | ||
1003 | Exit: | |
1004 | return error; | |
1005 | } | |
1006 | ||
1007 | ||
1008 | /* discard a face's autohint globals */ | |
1009 | void ah_hinter_done_face_globals( AH_Face_Globals* globals ) | |
1010 | { | |
1011 | FT_Face face = globals->face; | |
1012 | FT_Memory memory = face->memory; | |
1013 | ||
1014 | ||
1015 | FREE( globals ); | |
1016 | } | |
1017 | ||
1018 | ||
1019 | static | |
1020 | FT_Error ah_hinter_load( AH_Hinter* hinter, | |
1021 | FT_UInt glyph_index, | |
1022 | FT_UInt load_flags, | |
1023 | FT_UInt depth ) | |
1024 | { | |
1025 | FT_Face face = hinter->face; | |
1026 | FT_GlyphSlot slot = face->glyph; | |
1027 | FT_Fixed x_scale = face->size->metrics.x_scale; | |
1028 | FT_Fixed y_scale = face->size->metrics.y_scale; | |
1029 | FT_Glyph_Metrics metrics; /* temporary metrics */ | |
1030 | FT_Error error; | |
1031 | AH_Outline* outline = hinter->glyph; | |
1032 | AH_Loader* gloader = hinter->loader; | |
1033 | FT_Bool no_horz_hints = | |
1034 | ( load_flags & AH_HINT_NO_HORZ_EDGES ) != 0; | |
1035 | FT_Bool no_vert_hints = | |
1036 | ( load_flags & AH_HINT_NO_VERT_EDGES ) != 0; | |
1037 | ||
1038 | ||
1039 | /* load the glyph */ | |
1040 | error = FT_Load_Glyph( face, glyph_index, load_flags ); | |
1041 | if ( error ) | |
1042 | goto Exit; | |
1043 | ||
1044 | /* save current glyph metrics */ | |
1045 | metrics = slot->metrics; | |
1046 | ||
1047 | switch ( slot->format ) | |
1048 | { | |
1049 | case ft_glyph_format_outline: | |
1050 | /* first of all, copy the outline points in the loader's current */ | |
1051 | /* extra points, which is used to keep original glyph coordinates */ | |
1052 | error = ah_loader_check_points( gloader, slot->outline.n_points + 2, | |
1053 | slot->outline.n_contours ); | |
1054 | if ( error ) | |
1055 | goto Exit; | |
1056 | ||
1057 | MEM_Copy( gloader->current.extra_points, slot->outline.points, | |
1058 | slot->outline.n_points * sizeof ( FT_Vector ) ); | |
1059 | ||
1060 | MEM_Copy( gloader->current.outline.contours, slot->outline.contours, | |
1061 | slot->outline.n_contours * sizeof ( short ) ); | |
1062 | ||
1063 | MEM_Copy( gloader->current.outline.tags, slot->outline.tags, | |
1064 | slot->outline.n_points * sizeof ( char ) ); | |
1065 | ||
1066 | gloader->current.outline.n_points = slot->outline.n_points; | |
1067 | gloader->current.outline.n_contours = slot->outline.n_contours; | |
1068 | ||
1069 | /* compute original phantom points */ | |
1070 | hinter->pp1.x = 0; | |
1071 | hinter->pp1.y = 0; | |
1072 | hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale ); | |
1073 | hinter->pp2.y = 0; | |
1074 | ||
1075 | /* be sure to check for spacing glyphs */ | |
1076 | if ( slot->outline.n_points == 0 ) | |
1077 | goto Hint_Metrics; | |
1078 | ||
1079 | /* now, load the slot image into the auto-outline, and run the */ | |
1080 | /* automatic hinting process */ | |
1081 | error = ah_outline_load( outline, face ); /* XXX: change to slot */ | |
1082 | if ( error ) | |
1083 | goto Exit; | |
1084 | ||
1085 | /* perform feature detection */ | |
1086 | ah_outline_detect_features( outline ); | |
1087 | ||
1088 | if ( !no_horz_hints ) | |
1089 | { | |
1090 | ah_outline_compute_blue_edges( outline, hinter->globals ); | |
1091 | ah_outline_scale_blue_edges( outline, hinter->globals ); | |
1092 | } | |
1093 | ||
1094 | /* perform alignment control */ | |
1095 | ah_hinter_hint_edges( hinter, no_horz_hints, no_vert_hints ); | |
1096 | ah_hinter_align( hinter ); | |
1097 | ||
1098 | /* now save the current outline into the loader's current table */ | |
1099 | ah_outline_save( outline, gloader ); | |
1100 | ||
1101 | /* we now need to hint the metrics according to the change in */ | |
1102 | /* width/positioning that occured during the hinting process */ | |
1103 | { | |
1104 | FT_Pos old_width, new_width; | |
1105 | FT_Pos old_advance, new_advance; | |
1106 | FT_Pos old_lsb, new_lsb; | |
1107 | AH_Edge* edge1 = outline->vert_edges; /* leftmost edge */ | |
1108 | AH_Edge* edge2 = edge1 + | |
1109 | outline->num_vedges - 1; /* rightmost edge */ | |
1110 | ||
1111 | ||
1112 | old_width = edge2->opos - edge1->opos; | |
1113 | new_width = edge2->pos - edge1->pos; | |
1114 | ||
1115 | old_advance = hinter->pp2.x; | |
1116 | old_lsb = edge1->opos; | |
1117 | new_lsb = edge1->pos; | |
1118 | ||
1119 | new_advance = old_advance + | |
1120 | ( new_width + new_lsb - old_width - old_lsb ); | |
1121 | ||
1122 | hinter->pp1.x = ( ( new_lsb - old_lsb ) + 32 ) & -64; | |
1123 | hinter->pp2.x = ( ( edge2->pos + | |
1124 | ( old_advance - edge2->opos ) ) + 32 ) & -64; | |
1125 | } | |
1126 | ||
1127 | /* good, we simply add the glyph to our loader's base */ | |
1128 | ah_loader_add( gloader ); | |
1129 | break; | |
1130 | ||
1131 | case ft_glyph_format_composite: | |
1132 | { | |
1133 | FT_UInt nn, num_subglyphs = slot->num_subglyphs; | |
1134 | FT_UInt num_base_subgs, start_point, start_contour; | |
1135 | FT_SubGlyph* subglyph; | |
1136 | ||
1137 | ||
1138 | start_point = gloader->base.outline.n_points; | |
1139 | start_contour = gloader->base.outline.n_contours; | |
1140 | ||
1141 | /* first of all, copy the subglyph descriptors in the glyph loader */ | |
1142 | error = ah_loader_check_subglyphs( gloader, num_subglyphs ); | |
1143 | if ( error ) | |
1144 | goto Exit; | |
1145 | ||
1146 | MEM_Copy( gloader->current.subglyphs, slot->subglyphs, | |
1147 | num_subglyphs * sizeof ( FT_SubGlyph ) ); | |
1148 | ||
1149 | gloader->current.num_subglyphs = num_subglyphs; | |
1150 | num_base_subgs = gloader->base.num_subglyphs; | |
1151 | ||
1152 | /* now, read each subglyph independently */ | |
1153 | for ( nn = 0; nn < num_subglyphs; nn++ ) | |
1154 | { | |
1155 | FT_Vector pp1, pp2; | |
1156 | FT_Pos x, y; | |
1157 | FT_UInt num_points, num_new_points, num_base_points; | |
1158 | ||
1159 | ||
1160 | /* gloader.current.subglyphs can change during glyph loading due */ | |
1161 | /* to re-allocation -- we must recompute the current subglyph on */ | |
1162 | /* each iteration */ | |
1163 | subglyph = gloader->base.subglyphs + num_base_subgs + nn; | |
1164 | ||
1165 | pp1 = hinter->pp1; | |
1166 | pp2 = hinter->pp2; | |
1167 | ||
1168 | num_base_points = gloader->base.outline.n_points; | |
1169 | ||
1170 | error = ah_hinter_load( hinter, subglyph->index, | |
1171 | load_flags, depth + 1 ); | |
1172 | if ( error ) | |
1173 | goto Exit; | |
1174 | ||
1175 | /* recompute subglyph pointer */ | |
1176 | subglyph = gloader->base.subglyphs + num_base_subgs + nn; | |
1177 | ||
1178 | if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS ) | |
1179 | { | |
1180 | pp1 = hinter->pp1; | |
1181 | pp2 = hinter->pp2; | |
1182 | } | |
1183 | else | |
1184 | { | |
1185 | hinter->pp1 = pp1; | |
1186 | hinter->pp2 = pp2; | |
1187 | } | |
1188 | ||
1189 | num_points = gloader->base.outline.n_points; | |
1190 | num_new_points = num_points - num_base_points; | |
1191 | ||
1192 | /* now perform the transform required for this subglyph */ | |
1193 | ||
1194 | if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE | | |
1195 | FT_SUBGLYPH_FLAG_XY_SCALE | | |
1196 | FT_SUBGLYPH_FLAG_2X2 ) ) | |
1197 | { | |
1198 | FT_Vector* cur = gloader->base.outline.points + | |
1199 | num_base_points; | |
1200 | FT_Vector* org = gloader->base.extra_points + | |
1201 | num_base_points; | |
1202 | FT_Vector* limit = cur + num_new_points; | |
1203 | ||
1204 | ||
1205 | for ( ; cur < limit; cur++, org++ ) | |
1206 | { | |
1207 | FT_Vector_Transform( cur, &subglyph->transform ); | |
1208 | FT_Vector_Transform( org, &subglyph->transform ); | |
1209 | } | |
1210 | } | |
1211 | ||
1212 | /* apply offset */ | |
1213 | ||
1214 | if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) ) | |
1215 | { | |
1216 | FT_Int k = subglyph->arg1; | |
1217 | FT_UInt l = subglyph->arg2; | |
1218 | FT_Vector* p1; | |
1219 | FT_Vector* p2; | |
1220 | ||
1221 | ||
1222 | if ( start_point + k >= num_base_points || | |
1223 | l >= (FT_UInt)num_new_points ) | |
1224 | { | |
1225 | error = FT_Err_Invalid_Composite; | |
1226 | goto Exit; | |
1227 | } | |
1228 | ||
1229 | l += num_base_points; | |
1230 | ||
1231 | /* for now, only use the current point coordinates */ | |
1232 | /* we may consider another approach in the near future */ | |
1233 | p1 = gloader->base.outline.points + start_point + k; | |
1234 | p2 = gloader->base.outline.points + start_point + l; | |
1235 | ||
1236 | x = p1->x - p2->x; | |
1237 | y = p1->y - p2->y; | |
1238 | } | |
1239 | else | |
1240 | { | |
1241 | x = FT_MulFix( subglyph->arg1, x_scale ); | |
1242 | y = FT_MulFix( subglyph->arg2, y_scale ); | |
1243 | ||
1244 | x = ( x + 32 ) & -64; | |
1245 | y = ( y + 32 ) & -64; | |
1246 | } | |
1247 | ||
1248 | { | |
1249 | FT_Outline dummy = gloader->base.outline; | |
1250 | ||
1251 | ||
1252 | dummy.points += num_base_points; | |
1253 | dummy.n_points = num_new_points; | |
1254 | ||
1255 | FT_Outline_Translate( &dummy, x, y ); | |
1256 | } | |
1257 | } | |
1258 | } | |
1259 | break; | |
1260 | ||
1261 | default: | |
1262 | /* we don't support other formats (yet?) */ | |
1263 | error = FT_Err_Unimplemented_Feature; | |
1264 | } | |
1265 | ||
1266 | Hint_Metrics: | |
1267 | if ( depth == 0 ) | |
1268 | { | |
1269 | FT_BBox bbox; | |
1270 | ||
1271 | ||
1272 | /* we must translate our final outline by -pp1.x, and compute */ | |
1273 | /* the new metrics */ | |
1274 | if ( hinter->pp1.x ) | |
1275 | FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 ); | |
1276 | ||
1277 | FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); | |
1278 | bbox.xMin &= -64; | |
1279 | bbox.yMin &= -64; | |
1280 | bbox.xMax = ( bbox.xMax + 63 ) & -64; | |
1281 | bbox.yMax = ( bbox.yMax + 63 ) & -64; | |
1282 | ||
1283 | slot->metrics.width = bbox.xMax - bbox.xMin; | |
1284 | slot->metrics.height = bbox.yMax - bbox.yMin; | |
1285 | slot->metrics.horiBearingX = bbox.xMin; | |
1286 | slot->metrics.horiBearingY = bbox.yMax; | |
1287 | slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x; | |
1288 | /* XXX: TO DO - slot->linearHoriAdvance */ | |
1289 | ||
1290 | /* now copy outline into glyph slot */ | |
1291 | ah_loader_rewind( slot->loader ); | |
1292 | error = ah_loader_copy_points( slot->loader, gloader ); | |
1293 | if ( error ) | |
1294 | goto Exit; | |
1295 | ||
1296 | slot->outline = slot->loader->base.outline; | |
1297 | slot->format = ft_glyph_format_outline; | |
1298 | } | |
1299 | ||
1300 | Exit: | |
1301 | return error; | |
1302 | } | |
1303 | ||
1304 | ||
1305 | /* load and hint a given glyph */ | |
1306 | FT_Error ah_hinter_load_glyph( AH_Hinter* hinter, | |
1307 | FT_GlyphSlot slot, | |
1308 | FT_Size size, | |
1309 | FT_UInt glyph_index, | |
1310 | FT_Int load_flags ) | |
1311 | { | |
1312 | FT_Face face = slot->face; | |
1313 | FT_Error error; | |
1314 | FT_Fixed x_scale = size->metrics.x_scale; | |
1315 | FT_Fixed y_scale = size->metrics.y_scale; | |
1316 | AH_Face_Globals* face_globals = FACE_GLOBALS( face ); | |
1317 | ||
1318 | ||
1319 | /* first of all, we need to check that we're using the correct face and */ | |
1320 | /* global hints to load the glyph */ | |
1321 | if ( hinter->face != face || hinter->globals != face_globals ) | |
1322 | { | |
1323 | hinter->face = face; | |
1324 | if ( !face_globals ) | |
1325 | { | |
1326 | error = ah_hinter_new_face_globals( hinter, face, 0 ); | |
1327 | if ( error ) | |
1328 | goto Exit; | |
1329 | } | |
1330 | hinter->globals = FACE_GLOBALS( face ); | |
1331 | face_globals = FACE_GLOBALS( face ); | |
1332 | } | |
1333 | ||
1334 | /* now, we must check the current character pixel size to see if we */ | |
1335 | /* need to rescale the global metrics */ | |
1336 | if ( face_globals->x_scale != x_scale || | |
1337 | face_globals->y_scale != y_scale ) | |
1338 | ah_hinter_scale_globals( hinter, x_scale, y_scale ); | |
1339 | ||
1340 | load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE; | |
1341 | ||
1342 | ah_loader_rewind( hinter->loader ); | |
1343 | ||
1344 | error = ah_hinter_load( hinter, glyph_index, load_flags, 0 ); | |
1345 | ||
1346 | Exit: | |
1347 | return error; | |
1348 | } | |
1349 | ||
1350 | ||
1351 | /* retrieve a face's autohint globals for client applications */ | |
1352 | void ah_hinter_get_global_hints( AH_Hinter* hinter, | |
1353 | FT_Face face, | |
1354 | void** global_hints, | |
1355 | long* global_len ) | |
1356 | { | |
1357 | AH_Globals* globals = 0; | |
1358 | FT_Memory memory = hinter->memory; | |
1359 | FT_Error error; | |
1360 | ||
1361 | ||
1362 | /* allocate new master globals */ | |
1363 | if ( ALLOC( globals, sizeof ( *globals ) ) ) | |
1364 | goto Fail; | |
1365 | ||
1366 | /* compute face globals if needed */ | |
1367 | if ( !FACE_GLOBALS( face ) ) | |
1368 | { | |
1369 | error = ah_hinter_new_face_globals( hinter, face, 0 ); | |
1370 | if ( error ) | |
1371 | goto Fail; | |
1372 | } | |
1373 | ||
1374 | *globals = FACE_GLOBALS( face )->design; | |
1375 | *global_hints = globals; | |
1376 | *global_len = sizeof( *globals ); | |
1377 | ||
1378 | return; | |
1379 | ||
1380 | Fail: | |
1381 | FREE( globals ); | |
1382 | ||
1383 | *global_hints = 0; | |
1384 | *global_len = 0; | |
1385 | } | |
1386 | ||
1387 | ||
1388 | void ah_hinter_done_global_hints( AH_Hinter* hinter, | |
1389 | void* global_hints ) | |
1390 | { | |
1391 | FT_Memory memory = hinter->memory; | |
1392 | ||
1393 | ||
1394 | FREE( global_hints ); | |
1395 | } | |
1396 | ||
1397 | ||
1398 | /* END */ |