[FREETYPE] Update to v2.6.2. CORE-10614
[reactos.git] / reactos / lib / 3rdparty / freetype / src / autofit / aflatin.c
1 /***************************************************************************/
2 /* */
3 /* aflatin.c */
4 /* */
5 /* Auto-fitter hinting routines for latin writing system (body). */
6 /* */
7 /* Copyright 2003-2015 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
9 /* */
10 /* This file is part of the FreeType project, and may only be used, */
11 /* modified, and distributed under the terms of the FreeType project */
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13 /* this file you indicate that you have read the license and */
14 /* understand and accept it fully. */
15 /* */
16 /***************************************************************************/
17
18
19 #include <ft2build.h>
20 #include FT_ADVANCES_H
21 #include FT_INTERNAL_DEBUG_H
22
23 #include "afglobal.h"
24 #include "afpic.h"
25 #include "aflatin.h"
26 #include "aferrors.h"
27
28
29 #ifdef AF_CONFIG_OPTION_USE_WARPER
30 #include "afwarp.h"
31 #endif
32
33
34 /*************************************************************************/
35 /* */
36 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
37 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
38 /* messages during execution. */
39 /* */
40 #undef FT_COMPONENT
41 #define FT_COMPONENT trace_aflatin
42
43
44 /* needed for computation of round vs. flat segments */
45 #define FLAT_THRESHOLD( x ) ( x / 14 )
46
47
48 /*************************************************************************/
49 /*************************************************************************/
50 /***** *****/
51 /***** L A T I N G L O B A L M E T R I C S *****/
52 /***** *****/
53 /*************************************************************************/
54 /*************************************************************************/
55
56
57 /* Find segments and links, compute all stem widths, and initialize */
58 /* standard width and height for the glyph with given charcode. */
59
60 FT_LOCAL_DEF( void )
61 af_latin_metrics_init_widths( AF_LatinMetrics metrics,
62 FT_Face face )
63 {
64 /* scan the array of segments in each direction */
65 #ifdef __REACTOS__
66 AF_GlyphHintsRec *hints = malloc(sizeof(AF_GlyphHintsRec));
67 #else
68 AF_GlyphHintsRec hints[1];
69 #endif
70
71
72 FT_TRACE5(( "\n"
73 "latin standard widths computation (style `%s')\n"
74 "=====================================================\n"
75 "\n",
76 af_style_names[metrics->root.style_class->style] ));
77
78 af_glyph_hints_init( hints, face->memory );
79
80 metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
81 metrics->axis[AF_DIMENSION_VERT].width_count = 0;
82
83 {
84 FT_Error error;
85 FT_ULong glyph_index;
86 FT_Long y_offset;
87 int dim;
88 #ifdef __REACTOS__
89 AF_LatinMetricsRec *dummy = malloc(sizeof(AF_LatinMetricsRec));
90 #else
91 AF_LatinMetricsRec dummy[1];
92 #endif
93 AF_Scaler scaler = &dummy->root.scaler;
94
95 #ifdef FT_CONFIG_OPTION_PIC
96 AF_FaceGlobals globals = metrics->root.globals;
97 #endif
98
99 AF_StyleClass style_class = metrics->root.style_class;
100 AF_ScriptClass script_class = AF_SCRIPT_CLASSES_GET
101 [style_class->script];
102
103 FT_UInt32 standard_char;
104
105
106 /*
107 * We check more than a single standard character to catch features
108 * like `c2sc' (small caps from caps) that don't contain lowercase
109 * letters by definition, or other features that mainly operate on
110 * numerals.
111 */
112
113 standard_char = script_class->standard_char1;
114 af_get_char_index( &metrics->root,
115 standard_char,
116 &glyph_index,
117 &y_offset );
118 if ( !glyph_index )
119 {
120 if ( script_class->standard_char2 )
121 {
122 standard_char = script_class->standard_char2;
123 af_get_char_index( &metrics->root,
124 standard_char,
125 &glyph_index,
126 &y_offset );
127 if ( !glyph_index )
128 {
129 if ( script_class->standard_char3 )
130 {
131 standard_char = script_class->standard_char3;
132 af_get_char_index( &metrics->root,
133 standard_char,
134 &glyph_index,
135 &y_offset );
136 if ( !glyph_index )
137 goto Exit;
138 }
139 else
140 goto Exit;
141 }
142 }
143 else
144 goto Exit;
145 }
146
147 FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n",
148 standard_char, glyph_index ));
149
150 error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
151 if ( error || face->glyph->outline.n_points <= 0 )
152 goto Exit;
153
154 FT_ZERO( dummy );
155
156 dummy->units_per_em = metrics->units_per_em;
157
158 scaler->x_scale = 0x10000L;
159 scaler->y_scale = 0x10000L;
160 scaler->x_delta = 0;
161 scaler->y_delta = 0;
162
163 scaler->face = face;
164 scaler->render_mode = FT_RENDER_MODE_NORMAL;
165 scaler->flags = 0;
166
167 af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy );
168
169 error = af_glyph_hints_reload( hints, &face->glyph->outline );
170 if ( error )
171 goto Exit;
172
173 for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
174 {
175 AF_LatinAxis axis = &metrics->axis[dim];
176 AF_AxisHints axhints = &hints->axis[dim];
177 AF_Segment seg, limit, link;
178 FT_UInt num_widths = 0;
179
180
181 error = af_latin_hints_compute_segments( hints,
182 (AF_Dimension)dim );
183 if ( error )
184 goto Exit;
185
186 /*
187 * We assume that the glyphs selected for the stem width
188 * computation are `featureless' enough so that the linking
189 * algorithm works fine without adjustments of its scoring
190 * function.
191 */
192 af_latin_hints_link_segments( hints,
193 0,
194 NULL,
195 (AF_Dimension)dim );
196
197 seg = axhints->segments;
198 limit = seg + axhints->num_segments;
199
200 for ( ; seg < limit; seg++ )
201 {
202 link = seg->link;
203
204 /* we only consider stem segments there! */
205 if ( link && link->link == seg && link > seg )
206 {
207 FT_Pos dist;
208
209
210 dist = seg->pos - link->pos;
211 if ( dist < 0 )
212 dist = -dist;
213
214 if ( num_widths < AF_LATIN_MAX_WIDTHS )
215 axis->widths[num_widths++].org = dist;
216 }
217 }
218
219 /* this also replaces multiple almost identical stem widths */
220 /* with a single one (the value 100 is heuristic) */
221 af_sort_and_quantize_widths( &num_widths, axis->widths,
222 dummy->units_per_em / 100 );
223 axis->width_count = num_widths;
224 }
225
226 Exit:
227 for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
228 {
229 AF_LatinAxis axis = &metrics->axis[dim];
230 FT_Pos stdw;
231
232
233 stdw = ( axis->width_count > 0 ) ? axis->widths[0].org
234 : AF_LATIN_CONSTANT( metrics, 50 );
235
236 /* let's try 20% of the smallest width */
237 axis->edge_distance_threshold = stdw / 5;
238 axis->standard_width = stdw;
239 axis->extra_light = 0;
240
241 #ifdef FT_DEBUG_LEVEL_TRACE
242 {
243 FT_UInt i;
244
245
246 FT_TRACE5(( "%s widths:\n",
247 dim == AF_DIMENSION_VERT ? "horizontal"
248 : "vertical" ));
249
250 FT_TRACE5(( " %d (standard)", axis->standard_width ));
251 for ( i = 1; i < axis->width_count; i++ )
252 FT_TRACE5(( " %d", axis->widths[i].org ));
253
254 FT_TRACE5(( "\n" ));
255 }
256 #endif
257 }
258 #ifdef __REACTOS__
259 free(dummy);
260 #endif
261 }
262
263 FT_TRACE5(( "\n" ));
264
265 af_glyph_hints_done( hints );
266
267 #ifdef __REACTOS__
268 free(hints);
269 #endif
270
271 }
272
273
274 /* Find all blue zones. Flat segments give the reference points, */
275 /* round segments the overshoot positions. */
276
277 static void
278 af_latin_metrics_init_blues( AF_LatinMetrics metrics,
279 FT_Face face )
280 {
281 FT_Pos flats [AF_BLUE_STRING_MAX_LEN];
282 FT_Pos rounds[AF_BLUE_STRING_MAX_LEN];
283
284 FT_UInt num_flats;
285 FT_UInt num_rounds;
286
287 AF_LatinBlue blue;
288 FT_Error error;
289 AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT];
290 FT_Outline outline;
291
292 AF_StyleClass sc = metrics->root.style_class;
293
294 AF_Blue_Stringset bss = sc->blue_stringset;
295 const AF_Blue_StringRec* bs = &af_blue_stringsets[bss];
296
297 FT_Pos flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
298
299
300 /* we walk over the blue character strings as specified in the */
301 /* style's entry in the `af_blue_stringset' array */
302
303 FT_TRACE5(( "latin blue zones computation\n"
304 "============================\n"
305 "\n" ));
306
307 for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
308 {
309 const char* p = &af_blue_strings[bs->string];
310 FT_Pos* blue_ref;
311 FT_Pos* blue_shoot;
312 FT_Pos ascender;
313 FT_Pos descender;
314
315
316 #ifdef FT_DEBUG_LEVEL_TRACE
317 {
318 FT_Bool have_flag = 0;
319
320
321 FT_TRACE5(( "blue zone %d", axis->blue_count ));
322
323 if ( bs->properties )
324 {
325 FT_TRACE5(( " (" ));
326
327 if ( AF_LATIN_IS_TOP_BLUE( bs ) )
328 {
329 FT_TRACE5(( "top" ));
330 have_flag = 1;
331 }
332
333 if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
334 {
335 if ( have_flag )
336 FT_TRACE5(( ", " ));
337 FT_TRACE5(( "neutral" ));
338 have_flag = 1;
339 }
340
341 if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) )
342 {
343 if ( have_flag )
344 FT_TRACE5(( ", " ));
345 FT_TRACE5(( "small top" ));
346 have_flag = 1;
347 }
348
349 if ( AF_LATIN_IS_LONG_BLUE( bs ) )
350 {
351 if ( have_flag )
352 FT_TRACE5(( ", " ));
353 FT_TRACE5(( "long" ));
354 }
355
356 FT_TRACE5(( ")" ));
357 }
358
359 FT_TRACE5(( ":\n" ));
360 }
361 #endif /* FT_DEBUG_LEVEL_TRACE */
362
363 num_flats = 0;
364 num_rounds = 0;
365 ascender = 0;
366 descender = 0;
367
368 while ( *p )
369 {
370 FT_ULong ch;
371 FT_ULong glyph_index;
372 FT_Long y_offset;
373 FT_Pos best_y; /* same as points.y */
374 FT_Int best_point, best_contour_first, best_contour_last;
375 FT_Vector* points;
376 FT_Bool round = 0;
377
378
379 GET_UTF8_CHAR( ch, p );
380
381 /* load the character in the face -- skip unknown or empty ones */
382 af_get_char_index( &metrics->root, ch, &glyph_index, &y_offset );
383 if ( glyph_index == 0 )
384 {
385 FT_TRACE5(( " U+%04lX unavailable\n", ch ));
386 continue;
387 }
388
389 error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
390 outline = face->glyph->outline;
391 /* reject glyphs that don't produce any rendering */
392 if ( error || outline.n_points <= 2 )
393 {
394 FT_TRACE5(( " U+%04lX contains no (usable) outlines\n", ch ));
395 continue;
396 }
397
398 /* now compute min or max point indices and coordinates */
399 points = outline.points;
400 best_point = -1;
401 best_y = 0; /* make compiler happy */
402 best_contour_first = 0; /* ditto */
403 best_contour_last = 0; /* ditto */
404
405 {
406 FT_Int nn;
407 FT_Int first = 0;
408 FT_Int last = -1;
409
410
411 for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ )
412 {
413 FT_Int old_best_point = best_point;
414 FT_Int pp;
415
416
417 last = outline.contours[nn];
418
419 /* Avoid single-point contours since they are never rasterized. */
420 /* In some fonts, they correspond to mark attachment points */
421 /* that are way outside of the glyph's real outline. */
422 if ( last <= first )
423 continue;
424
425 if ( AF_LATIN_IS_TOP_BLUE( bs ) )
426 {
427 for ( pp = first; pp <= last; pp++ )
428 {
429 if ( best_point < 0 || points[pp].y > best_y )
430 {
431 best_point = pp;
432 best_y = points[pp].y;
433 ascender = FT_MAX( ascender, best_y + y_offset );
434 }
435 else
436 descender = FT_MIN( descender, points[pp].y + y_offset );
437 }
438 }
439 else
440 {
441 for ( pp = first; pp <= last; pp++ )
442 {
443 if ( best_point < 0 || points[pp].y < best_y )
444 {
445 best_point = pp;
446 best_y = points[pp].y;
447 descender = FT_MIN( descender, best_y + y_offset );
448 }
449 else
450 ascender = FT_MAX( ascender, points[pp].y + y_offset );
451 }
452 }
453
454 if ( best_point != old_best_point )
455 {
456 best_contour_first = first;
457 best_contour_last = last;
458 }
459 }
460 }
461
462 /* now check whether the point belongs to a straight or round */
463 /* segment; we first need to find in which contour the extremum */
464 /* lies, then inspect its previous and next points */
465 if ( best_point >= 0 )
466 {
467 FT_Pos best_x = points[best_point].x;
468 FT_Int prev, next;
469 FT_Int best_segment_first, best_segment_last;
470 FT_Int best_on_point_first, best_on_point_last;
471 FT_Pos dist;
472
473
474 best_segment_first = best_point;
475 best_segment_last = best_point;
476
477 if ( FT_CURVE_TAG( outline.tags[best_point] ) == FT_CURVE_TAG_ON )
478 {
479 best_on_point_first = best_point;
480 best_on_point_last = best_point;
481 }
482 else
483 {
484 best_on_point_first = -1;
485 best_on_point_last = -1;
486 }
487
488 /* look for the previous and next points on the contour */
489 /* that are not on the same Y coordinate, then threshold */
490 /* the `closeness'... */
491 prev = best_point;
492 next = prev;
493
494 do
495 {
496 if ( prev > best_contour_first )
497 prev--;
498 else
499 prev = best_contour_last;
500
501 dist = FT_ABS( points[prev].y - best_y );
502 /* accept a small distance or a small angle (both values are */
503 /* heuristic; value 20 corresponds to approx. 2.9 degrees) */
504 if ( dist > 5 )
505 if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist )
506 break;
507
508 best_segment_first = prev;
509
510 if ( FT_CURVE_TAG( outline.tags[prev] ) == FT_CURVE_TAG_ON )
511 {
512 best_on_point_first = prev;
513 if ( best_on_point_last < 0 )
514 best_on_point_last = prev;
515 }
516
517 } while ( prev != best_point );
518
519 do
520 {
521 if ( next < best_contour_last )
522 next++;
523 else
524 next = best_contour_first;
525
526 dist = FT_ABS( points[next].y - best_y );
527 if ( dist > 5 )
528 if ( FT_ABS( points[next].x - best_x ) <= 20 * dist )
529 break;
530
531 best_segment_last = next;
532
533 if ( FT_CURVE_TAG( outline.tags[next] ) == FT_CURVE_TAG_ON )
534 {
535 best_on_point_last = next;
536 if ( best_on_point_first < 0 )
537 best_on_point_first = next;
538 }
539
540 } while ( next != best_point );
541
542 if ( AF_LATIN_IS_LONG_BLUE( bs ) )
543 {
544 /* If this flag is set, we have an additional constraint to */
545 /* get the blue zone distance: Find a segment of the topmost */
546 /* (or bottommost) contour that is longer than a heuristic */
547 /* threshold. This ensures that small bumps in the outline */
548 /* are ignored (for example, the `vertical serifs' found in */
549 /* many Hebrew glyph designs). */
550
551 /* If this segment is long enough, we are done. Otherwise, */
552 /* search the segment next to the extremum that is long */
553 /* enough, has the same direction, and a not too large */
554 /* vertical distance from the extremum. Note that the */
555 /* algorithm doesn't check whether the found segment is */
556 /* actually the one (vertically) nearest to the extremum. */
557
558 /* heuristic threshold value */
559 FT_Pos length_threshold = metrics->units_per_em / 25;
560
561
562 dist = FT_ABS( points[best_segment_last].x -
563 points[best_segment_first].x );
564
565 if ( dist < length_threshold &&
566 best_segment_last - best_segment_first + 2 <=
567 best_contour_last - best_contour_first )
568 {
569 /* heuristic threshold value */
570 FT_Pos height_threshold = metrics->units_per_em / 4;
571
572 FT_Int first;
573 FT_Int last;
574 FT_Bool hit;
575
576 /* we intentionally declare these two variables */
577 /* outside of the loop since various compilers emit */
578 /* incorrect warning messages otherwise, talking about */
579 /* `possibly uninitialized variables' */
580 FT_Int p_first = 0; /* make compiler happy */
581 FT_Int p_last = 0;
582
583 FT_Bool left2right;
584
585
586 /* compute direction */
587 prev = best_point;
588
589 do
590 {
591 if ( prev > best_contour_first )
592 prev--;
593 else
594 prev = best_contour_last;
595
596 if ( points[prev].x != best_x )
597 break;
598
599 } while ( prev != best_point );
600
601 /* skip glyph for the degenerate case */
602 if ( prev == best_point )
603 continue;
604
605 left2right = FT_BOOL( points[prev].x < points[best_point].x );
606
607 first = best_segment_last;
608 last = first;
609 hit = 0;
610
611 do
612 {
613 FT_Bool l2r;
614 FT_Pos d;
615
616
617 if ( !hit )
618 {
619 /* no hit; adjust first point */
620 first = last;
621
622 /* also adjust first and last on point */
623 if ( FT_CURVE_TAG( outline.tags[first] ) ==
624 FT_CURVE_TAG_ON )
625 {
626 p_first = first;
627 p_last = first;
628 }
629 else
630 {
631 p_first = -1;
632 p_last = -1;
633 }
634
635 hit = 1;
636 }
637
638 if ( last < best_contour_last )
639 last++;
640 else
641 last = best_contour_first;
642
643 if ( FT_ABS( best_y - points[first].y ) > height_threshold )
644 {
645 /* vertical distance too large */
646 hit = 0;
647 continue;
648 }
649
650 /* same test as above */
651 dist = FT_ABS( points[last].y - points[first].y );
652 if ( dist > 5 )
653 if ( FT_ABS( points[last].x - points[first].x ) <=
654 20 * dist )
655 {
656 hit = 0;
657 continue;
658 }
659
660 if ( FT_CURVE_TAG( outline.tags[last] ) == FT_CURVE_TAG_ON )
661 {
662 p_last = last;
663 if ( p_first < 0 )
664 p_first = last;
665 }
666
667 l2r = FT_BOOL( points[first].x < points[last].x );
668 d = FT_ABS( points[last].x - points[first].x );
669
670 if ( l2r == left2right &&
671 d >= length_threshold )
672 {
673 /* all constraints are met; update segment after finding */
674 /* its end */
675 do
676 {
677 if ( last < best_contour_last )
678 last++;
679 else
680 last = best_contour_first;
681
682 d = FT_ABS( points[last].y - points[first].y );
683 if ( d > 5 )
684 if ( FT_ABS( points[next].x - points[first].x ) <=
685 20 * dist )
686 {
687 if ( last > best_contour_first )
688 last--;
689 else
690 last = best_contour_last;
691 break;
692 }
693
694 p_last = last;
695
696 if ( FT_CURVE_TAG( outline.tags[last] ) ==
697 FT_CURVE_TAG_ON )
698 {
699 p_last = last;
700 if ( p_first < 0 )
701 p_first = last;
702 }
703
704 } while ( last != best_segment_first );
705
706 best_y = points[first].y;
707
708 best_segment_first = first;
709 best_segment_last = last;
710
711 best_on_point_first = p_first;
712 best_on_point_last = p_last;
713
714 break;
715 }
716
717 } while ( last != best_segment_first );
718 }
719 }
720
721 /* for computing blue zones, we add the y offset as returned */
722 /* by the currently used OpenType feature -- for example, */
723 /* superscript glyphs might be identical to subscript glyphs */
724 /* with a vertical shift */
725 best_y += y_offset;
726
727 FT_TRACE5(( " U+%04lX: best_y = %5ld", ch, best_y ));
728
729 /* now set the `round' flag depending on the segment's kind: */
730 /* */
731 /* - if the horizontal distance between the first and last */
732 /* `on' point is larger than a heuristic threshold */
733 /* we have a flat segment */
734 /* - if either the first or the last point of the segment is */
735 /* an `off' point, the segment is round, otherwise it is */
736 /* flat */
737 if ( best_on_point_first >= 0 &&
738 best_on_point_last >= 0 &&
739 ( FT_ABS( points[best_on_point_last].x -
740 points[best_on_point_first].x ) ) >
741 flat_threshold )
742 round = 0;
743 else
744 round = FT_BOOL(
745 FT_CURVE_TAG( outline.tags[best_segment_first] ) !=
746 FT_CURVE_TAG_ON ||
747 FT_CURVE_TAG( outline.tags[best_segment_last] ) !=
748 FT_CURVE_TAG_ON );
749
750 if ( round && AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
751 {
752 /* only use flat segments for a neutral blue zone */
753 FT_TRACE5(( " (round, skipped)\n" ));
754 continue;
755 }
756
757 FT_TRACE5(( " (%s)\n", round ? "round" : "flat" ));
758 }
759
760 if ( round )
761 rounds[num_rounds++] = best_y;
762 else
763 flats[num_flats++] = best_y;
764 }
765
766 if ( num_flats == 0 && num_rounds == 0 )
767 {
768 /*
769 * we couldn't find a single glyph to compute this blue zone,
770 * we will simply ignore it then
771 */
772 FT_TRACE5(( " empty\n" ));
773 continue;
774 }
775
776 /* we have computed the contents of the `rounds' and `flats' tables, */
777 /* now determine the reference and overshoot position of the blue -- */
778 /* we simply take the median value after a simple sort */
779 af_sort_pos( num_rounds, rounds );
780 af_sort_pos( num_flats, flats );
781
782 blue = &axis->blues[axis->blue_count];
783 blue_ref = &blue->ref.org;
784 blue_shoot = &blue->shoot.org;
785
786 axis->blue_count++;
787
788 if ( num_flats == 0 )
789 {
790 *blue_ref =
791 *blue_shoot = rounds[num_rounds / 2];
792 }
793 else if ( num_rounds == 0 )
794 {
795 *blue_ref =
796 *blue_shoot = flats[num_flats / 2];
797 }
798 else
799 {
800 *blue_ref = flats [num_flats / 2];
801 *blue_shoot = rounds[num_rounds / 2];
802 }
803
804 /* there are sometimes problems: if the overshoot position of top */
805 /* zones is under its reference position, or the opposite for bottom */
806 /* zones. We must thus check everything there and correct the errors */
807 if ( *blue_shoot != *blue_ref )
808 {
809 FT_Pos ref = *blue_ref;
810 FT_Pos shoot = *blue_shoot;
811 FT_Bool over_ref = FT_BOOL( shoot > ref );
812
813
814 if ( AF_LATIN_IS_TOP_BLUE( bs ) ^ over_ref )
815 {
816 *blue_ref =
817 *blue_shoot = ( shoot + ref ) / 2;
818
819 FT_TRACE5(( " [overshoot smaller than reference,"
820 " taking mean value]\n" ));
821 }
822 }
823
824 blue->ascender = ascender;
825 blue->descender = descender;
826
827 blue->flags = 0;
828 if ( AF_LATIN_IS_TOP_BLUE( bs ) )
829 blue->flags |= AF_LATIN_BLUE_TOP;
830 if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
831 blue->flags |= AF_LATIN_BLUE_NEUTRAL;
832
833 /*
834 * The following flag is used later to adjust the y and x scales
835 * in order to optimize the pixel grid alignment of the top of small
836 * letters.
837 */
838 if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) )
839 blue->flags |= AF_LATIN_BLUE_ADJUSTMENT;
840
841 FT_TRACE5(( " -> reference = %ld\n"
842 " overshoot = %ld\n",
843 *blue_ref, *blue_shoot ));
844 }
845
846 FT_TRACE5(( "\n" ));
847
848 return;
849 }
850
851
852 /* Check whether all ASCII digits have the same advance width. */
853
854 FT_LOCAL_DEF( void )
855 af_latin_metrics_check_digits( AF_LatinMetrics metrics,
856 FT_Face face )
857 {
858 FT_UInt i;
859 FT_Bool started = 0, same_width = 1;
860 FT_Fixed advance, old_advance = 0;
861
862
863 /* digit `0' is 0x30 in all supported charmaps */
864 for ( i = 0x30; i <= 0x39; i++ )
865 {
866 FT_ULong glyph_index;
867 FT_Long y_offset;
868
869
870 af_get_char_index( &metrics->root, i, &glyph_index, &y_offset );
871 if ( glyph_index == 0 )
872 continue;
873
874 if ( FT_Get_Advance( face, glyph_index,
875 FT_LOAD_NO_SCALE |
876 FT_LOAD_NO_HINTING |
877 FT_LOAD_IGNORE_TRANSFORM,
878 &advance ) )
879 continue;
880
881 if ( started )
882 {
883 if ( advance != old_advance )
884 {
885 same_width = 0;
886 break;
887 }
888 }
889 else
890 {
891 old_advance = advance;
892 started = 1;
893 }
894 }
895
896 metrics->root.digits_have_same_width = same_width;
897 }
898
899
900 /* Initialize global metrics. */
901
902 FT_LOCAL_DEF( FT_Error )
903 af_latin_metrics_init( AF_LatinMetrics metrics,
904 FT_Face face )
905 {
906 FT_CharMap oldmap = face->charmap;
907
908
909 metrics->units_per_em = face->units_per_EM;
910
911 if ( !FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
912 {
913 af_latin_metrics_init_widths( metrics, face );
914 af_latin_metrics_init_blues( metrics, face );
915 af_latin_metrics_check_digits( metrics, face );
916 }
917
918 FT_Set_Charmap( face, oldmap );
919 return FT_Err_Ok;
920 }
921
922
923 /* Adjust scaling value, then scale and shift widths */
924 /* and blue zones (if applicable) for given dimension. */
925
926 static void
927 af_latin_metrics_scale_dim( AF_LatinMetrics metrics,
928 AF_Scaler scaler,
929 AF_Dimension dim )
930 {
931 FT_Fixed scale;
932 FT_Pos delta;
933 AF_LatinAxis axis;
934 FT_UInt nn;
935
936
937 if ( dim == AF_DIMENSION_HORZ )
938 {
939 scale = scaler->x_scale;
940 delta = scaler->x_delta;
941 }
942 else
943 {
944 scale = scaler->y_scale;
945 delta = scaler->y_delta;
946 }
947
948 axis = &metrics->axis[dim];
949
950 if ( axis->org_scale == scale && axis->org_delta == delta )
951 return;
952
953 axis->org_scale = scale;
954 axis->org_delta = delta;
955
956 /*
957 * correct X and Y scale to optimize the alignment of the top of small
958 * letters to the pixel grid
959 */
960 {
961 AF_LatinAxis Axis = &metrics->axis[AF_DIMENSION_VERT];
962 AF_LatinBlue blue = NULL;
963
964
965 for ( nn = 0; nn < Axis->blue_count; nn++ )
966 {
967 if ( Axis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT )
968 {
969 blue = &Axis->blues[nn];
970 break;
971 }
972 }
973
974 if ( blue )
975 {
976 FT_Pos scaled;
977 FT_Pos threshold;
978 FT_Pos fitted;
979 FT_UInt limit;
980 FT_UInt ppem;
981
982
983 scaled = FT_MulFix( blue->shoot.org, scaler->y_scale );
984 ppem = metrics->root.scaler.face->size->metrics.x_ppem;
985 limit = metrics->root.globals->increase_x_height;
986 threshold = 40;
987
988 /* if the `increase-x-height' property is active, */
989 /* we round up much more often */
990 if ( limit &&
991 ppem <= limit &&
992 ppem >= AF_PROP_INCREASE_X_HEIGHT_MIN )
993 threshold = 52;
994
995 fitted = ( scaled + threshold ) & ~63;
996
997 if ( scaled != fitted )
998 {
999 #if 0
1000 if ( dim == AF_DIMENSION_HORZ )
1001 {
1002 if ( fitted < scaled )
1003 scale -= scale / 50; /* scale *= 0.98 */
1004 }
1005 else
1006 #endif
1007 if ( dim == AF_DIMENSION_VERT )
1008 {
1009 FT_Pos max_height;
1010 FT_Pos dist;
1011 FT_Fixed new_scale;
1012
1013
1014 new_scale = FT_MulDiv( scale, fitted, scaled );
1015
1016 /* the scaling should not change the result by more than two pixels */
1017 max_height = metrics->units_per_em;
1018
1019 for ( nn = 0; nn < Axis->blue_count; nn++ )
1020 {
1021 max_height = FT_MAX( max_height, Axis->blues[nn].ascender );
1022 max_height = FT_MAX( max_height, -Axis->blues[nn].descender );
1023 }
1024
1025 dist = FT_ABS( FT_MulFix( max_height, new_scale - scale ) );
1026 dist &= ~127;
1027
1028 if ( dist == 0 )
1029 {
1030 scale = new_scale;
1031
1032 FT_TRACE5((
1033 "af_latin_metrics_scale_dim:"
1034 " x height alignment (style `%s'):\n"
1035 " "
1036 " vertical scaling changed from %.4f to %.4f (by %d%%)\n"
1037 "\n",
1038 af_style_names[metrics->root.style_class->style],
1039 axis->org_scale / 65536.0,
1040 scale / 65536.0,
1041 ( fitted - scaled ) * 100 / scaled ));
1042 }
1043 #ifdef FT_DEBUG_LEVEL_TRACE
1044 else
1045 {
1046 FT_TRACE5((
1047 "af_latin_metrics_scale_dim:"
1048 " x height alignment (style `%s'):\n"
1049 " "
1050 " excessive vertical scaling abandoned\n"
1051 "\n",
1052 af_style_names[metrics->root.style_class->style] ));
1053 }
1054 #endif
1055 }
1056 }
1057 }
1058 }
1059
1060 axis->scale = scale;
1061 axis->delta = delta;
1062
1063 if ( dim == AF_DIMENSION_HORZ )
1064 {
1065 metrics->root.scaler.x_scale = scale;
1066 metrics->root.scaler.x_delta = delta;
1067 }
1068 else
1069 {
1070 metrics->root.scaler.y_scale = scale;
1071 metrics->root.scaler.y_delta = delta;
1072 }
1073
1074 FT_TRACE5(( "%s widths (style `%s')\n",
1075 dim == AF_DIMENSION_HORZ ? "horizontal" : "vertical",
1076 af_style_names[metrics->root.style_class->style] ));
1077
1078 /* scale the widths */
1079 for ( nn = 0; nn < axis->width_count; nn++ )
1080 {
1081 AF_Width width = axis->widths + nn;
1082
1083
1084 width->cur = FT_MulFix( width->org, scale );
1085 width->fit = width->cur;
1086
1087 FT_TRACE5(( " %d scaled to %.2f\n",
1088 width->org,
1089 width->cur / 64.0 ));
1090 }
1091
1092 FT_TRACE5(( "\n" ));
1093
1094 /* an extra-light axis corresponds to a standard width that is */
1095 /* smaller than 5/8 pixels */
1096 axis->extra_light =
1097 (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
1098
1099 #ifdef FT_DEBUG_LEVEL_TRACE
1100 if ( axis->extra_light )
1101 FT_TRACE5(( "`%s' style is extra light (at current resolution)\n"
1102 "\n",
1103 af_style_names[metrics->root.style_class->style] ));
1104 #endif
1105
1106 if ( dim == AF_DIMENSION_VERT )
1107 {
1108 #ifdef FT_DEBUG_LEVEL_TRACE
1109 if ( axis->blue_count )
1110 FT_TRACE5(( "blue zones (style `%s')\n",
1111 af_style_names[metrics->root.style_class->style] ));
1112 #endif
1113
1114 /* scale the blue zones */
1115 for ( nn = 0; nn < axis->blue_count; nn++ )
1116 {
1117 AF_LatinBlue blue = &axis->blues[nn];
1118 FT_Pos dist;
1119
1120
1121 blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta;
1122 blue->ref.fit = blue->ref.cur;
1123 blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
1124 blue->shoot.fit = blue->shoot.cur;
1125 blue->flags &= ~AF_LATIN_BLUE_ACTIVE;
1126
1127 /* a blue zone is only active if it is less than 3/4 pixels tall */
1128 dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
1129 if ( dist <= 48 && dist >= -48 )
1130 {
1131 #if 0
1132 FT_Pos delta1;
1133 #endif
1134 FT_Pos delta2;
1135
1136
1137 /* use discrete values for blue zone widths */
1138
1139 #if 0
1140
1141 /* generic, original code */
1142 delta1 = blue->shoot.org - blue->ref.org;
1143 delta2 = delta1;
1144 if ( delta1 < 0 )
1145 delta2 = -delta2;
1146
1147 delta2 = FT_MulFix( delta2, scale );
1148
1149 if ( delta2 < 32 )
1150 delta2 = 0;
1151 else if ( delta2 < 64 )
1152 delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
1153 else
1154 delta2 = FT_PIX_ROUND( delta2 );
1155
1156 if ( delta1 < 0 )
1157 delta2 = -delta2;
1158
1159 blue->ref.fit = FT_PIX_ROUND( blue->ref.cur );
1160 blue->shoot.fit = blue->ref.fit + delta2;
1161
1162 #else
1163
1164 /* simplified version due to abs(dist) <= 48 */
1165 delta2 = dist;
1166 if ( dist < 0 )
1167 delta2 = -delta2;
1168
1169 if ( delta2 < 32 )
1170 delta2 = 0;
1171 else if ( delta2 < 48 )
1172 delta2 = 32;
1173 else
1174 delta2 = 64;
1175
1176 if ( dist < 0 )
1177 delta2 = -delta2;
1178
1179 blue->ref.fit = FT_PIX_ROUND( blue->ref.cur );
1180 blue->shoot.fit = blue->ref.fit - delta2;
1181
1182 #endif
1183
1184 blue->flags |= AF_LATIN_BLUE_ACTIVE;
1185
1186 FT_TRACE5(( " reference %d: %d scaled to %.2f%s\n"
1187 " overshoot %d: %d scaled to %.2f%s\n",
1188 nn,
1189 blue->ref.org,
1190 blue->ref.fit / 64.0,
1191 blue->flags & AF_LATIN_BLUE_ACTIVE ? ""
1192 : " (inactive)",
1193 nn,
1194 blue->shoot.org,
1195 blue->shoot.fit / 64.0,
1196 blue->flags & AF_LATIN_BLUE_ACTIVE ? ""
1197 : " (inactive)" ));
1198 }
1199 }
1200 }
1201 }
1202
1203
1204 /* Scale global values in both directions. */
1205
1206 FT_LOCAL_DEF( void )
1207 af_latin_metrics_scale( AF_LatinMetrics metrics,
1208 AF_Scaler scaler )
1209 {
1210 metrics->root.scaler.render_mode = scaler->render_mode;
1211 metrics->root.scaler.face = scaler->face;
1212 metrics->root.scaler.flags = scaler->flags;
1213
1214 af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
1215 af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
1216 }
1217
1218
1219 /* Extract standard_width from writing system/script specific */
1220 /* metrics class. */
1221
1222 FT_LOCAL_DEF( void )
1223 af_latin_get_standard_widths( AF_LatinMetrics metrics,
1224 FT_Pos* stdHW,
1225 FT_Pos* stdVW )
1226 {
1227 if ( stdHW )
1228 *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
1229
1230 if ( stdVW )
1231 *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width;
1232 }
1233
1234
1235 /*************************************************************************/
1236 /*************************************************************************/
1237 /***** *****/
1238 /***** L A T I N G L Y P H A N A L Y S I S *****/
1239 /***** *****/
1240 /*************************************************************************/
1241 /*************************************************************************/
1242
1243
1244 /* Walk over all contours and compute its segments. */
1245
1246 FT_LOCAL_DEF( FT_Error )
1247 af_latin_hints_compute_segments( AF_GlyphHints hints,
1248 AF_Dimension dim )
1249 {
1250 AF_LatinMetrics metrics = (AF_LatinMetrics)hints->metrics;
1251 AF_AxisHints axis = &hints->axis[dim];
1252 FT_Memory memory = hints->memory;
1253 FT_Error error = FT_Err_Ok;
1254 AF_Segment segment = NULL;
1255 AF_SegmentRec seg0;
1256 AF_Point* contour = hints->contours;
1257 AF_Point* contour_limit = contour + hints->num_contours;
1258 AF_Direction major_dir, segment_dir;
1259
1260 FT_Pos flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
1261
1262
1263 FT_ZERO( &seg0 );
1264 seg0.score = 32000;
1265 seg0.flags = AF_EDGE_NORMAL;
1266
1267 major_dir = (AF_Direction)FT_ABS( axis->major_dir );
1268 segment_dir = major_dir;
1269
1270 axis->num_segments = 0;
1271
1272 /* set up (u,v) in each point */
1273 if ( dim == AF_DIMENSION_HORZ )
1274 {
1275 AF_Point point = hints->points;
1276 AF_Point limit = point + hints->num_points;
1277
1278
1279 for ( ; point < limit; point++ )
1280 {
1281 point->u = point->fx;
1282 point->v = point->fy;
1283 }
1284 }
1285 else
1286 {
1287 AF_Point point = hints->points;
1288 AF_Point limit = point + hints->num_points;
1289
1290
1291 for ( ; point < limit; point++ )
1292 {
1293 point->u = point->fy;
1294 point->v = point->fx;
1295 }
1296 }
1297
1298 /* do each contour separately */
1299 for ( ; contour < contour_limit; contour++ )
1300 {
1301 AF_Point point = contour[0];
1302 AF_Point last = point->prev;
1303 int on_edge = 0;
1304 FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
1305 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
1306 FT_Pos min_on_pos = 32000;
1307 FT_Pos max_on_pos = -32000;
1308 FT_Bool passed;
1309
1310
1311 if ( point == last ) /* skip singletons -- just in case */
1312 continue;
1313
1314 if ( FT_ABS( last->out_dir ) == major_dir &&
1315 FT_ABS( point->out_dir ) == major_dir )
1316 {
1317 /* we are already on an edge, try to locate its start */
1318 last = point;
1319
1320 for (;;)
1321 {
1322 point = point->prev;
1323 if ( FT_ABS( point->out_dir ) != major_dir )
1324 {
1325 point = point->next;
1326 break;
1327 }
1328 if ( point == last )
1329 break;
1330 }
1331 }
1332
1333 last = point;
1334 passed = 0;
1335
1336 for (;;)
1337 {
1338 FT_Pos u, v;
1339
1340
1341 if ( on_edge )
1342 {
1343 u = point->u;
1344 if ( u < min_pos )
1345 min_pos = u;
1346 if ( u > max_pos )
1347 max_pos = u;
1348
1349 /* get minimum and maximum coordinate of on points */
1350 if ( !( point->flags & AF_FLAG_CONTROL ) )
1351 {
1352 v = point->v;
1353 if ( v < min_on_pos )
1354 min_on_pos = v;
1355 if ( v > max_on_pos )
1356 max_on_pos = v;
1357 }
1358
1359 if ( point->out_dir != segment_dir || point == last )
1360 {
1361 /* we are just leaving an edge; record a new segment! */
1362 segment->last = point;
1363 segment->pos = (FT_Short)( ( min_pos + max_pos ) >> 1 );
1364
1365 /* a segment is round if either its first or last point */
1366 /* is a control point, and the length of the on points */
1367 /* inbetween doesn't exceed a heuristic limit */
1368 if ( ( segment->first->flags | point->flags ) & AF_FLAG_CONTROL &&
1369 ( max_on_pos - min_on_pos ) < flat_threshold )
1370 segment->flags |= AF_EDGE_ROUND;
1371
1372 /* compute segment size */
1373 min_pos = max_pos = point->v;
1374
1375 v = segment->first->v;
1376 if ( v < min_pos )
1377 min_pos = v;
1378 if ( v > max_pos )
1379 max_pos = v;
1380
1381 segment->min_coord = (FT_Short)min_pos;
1382 segment->max_coord = (FT_Short)max_pos;
1383 segment->height = (FT_Short)( segment->max_coord -
1384 segment->min_coord );
1385
1386 on_edge = 0;
1387 segment = NULL;
1388 /* fall through */
1389 }
1390 }
1391
1392 /* now exit if we are at the start/end point */
1393 if ( point == last )
1394 {
1395 if ( passed )
1396 break;
1397 passed = 1;
1398 }
1399
1400 if ( !on_edge && FT_ABS( point->out_dir ) == major_dir )
1401 {
1402 /* this is the start of a new segment! */
1403 segment_dir = (AF_Direction)point->out_dir;
1404
1405 error = af_axis_hints_new_segment( axis, memory, &segment );
1406 if ( error )
1407 goto Exit;
1408
1409 /* clear all segment fields */
1410 segment[0] = seg0;
1411
1412 segment->dir = (FT_Char)segment_dir;
1413 segment->first = point;
1414 segment->last = point;
1415
1416 min_pos = max_pos = point->u;
1417
1418 if ( point->flags & AF_FLAG_CONTROL )
1419 {
1420 min_on_pos = 32000;
1421 max_on_pos = -32000;
1422 }
1423 else
1424 min_on_pos = max_on_pos = point->v;
1425
1426 on_edge = 1;
1427 }
1428
1429 point = point->next;
1430 }
1431
1432 } /* contours */
1433
1434
1435 /* now slightly increase the height of segments if this makes */
1436 /* sense -- this is used to better detect and ignore serifs */
1437 {
1438 AF_Segment segments = axis->segments;
1439 AF_Segment segments_end = segments + axis->num_segments;
1440
1441
1442 for ( segment = segments; segment < segments_end; segment++ )
1443 {
1444 AF_Point first = segment->first;
1445 AF_Point last = segment->last;
1446 FT_Pos first_v = first->v;
1447 FT_Pos last_v = last->v;
1448
1449
1450 if ( first_v < last_v )
1451 {
1452 AF_Point p;
1453
1454
1455 p = first->prev;
1456 if ( p->v < first_v )
1457 segment->height = (FT_Short)( segment->height +
1458 ( ( first_v - p->v ) >> 1 ) );
1459
1460 p = last->next;
1461 if ( p->v > last_v )
1462 segment->height = (FT_Short)( segment->height +
1463 ( ( p->v - last_v ) >> 1 ) );
1464 }
1465 else
1466 {
1467 AF_Point p;
1468
1469
1470 p = first->prev;
1471 if ( p->v > first_v )
1472 segment->height = (FT_Short)( segment->height +
1473 ( ( p->v - first_v ) >> 1 ) );
1474
1475 p = last->next;
1476 if ( p->v < last_v )
1477 segment->height = (FT_Short)( segment->height +
1478 ( ( last_v - p->v ) >> 1 ) );
1479 }
1480 }
1481 }
1482
1483 Exit:
1484 return error;
1485 }
1486
1487
1488 /* Link segments to form stems and serifs. If `width_count' and */
1489 /* `widths' are non-zero, use them to fine-tune the scoring function. */
1490
1491 FT_LOCAL_DEF( void )
1492 af_latin_hints_link_segments( AF_GlyphHints hints,
1493 FT_UInt width_count,
1494 AF_WidthRec* widths,
1495 AF_Dimension dim )
1496 {
1497 AF_AxisHints axis = &hints->axis[dim];
1498 AF_Segment segments = axis->segments;
1499 AF_Segment segment_limit = segments + axis->num_segments;
1500 FT_Pos len_threshold, len_score, dist_score, max_width;
1501 AF_Segment seg1, seg2;
1502
1503
1504 if ( width_count )
1505 max_width = widths[width_count - 1].org;
1506 else
1507 max_width = 0;
1508
1509 /* a heuristic value to set up a minimum value for overlapping */
1510 len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
1511 if ( len_threshold == 0 )
1512 len_threshold = 1;
1513
1514 /* a heuristic value to weight lengths */
1515 len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 );
1516
1517 /* a heuristic value to weight distances (no call to */
1518 /* AF_LATIN_CONSTANT needed, since we work on multiples */
1519 /* of the stem width) */
1520 dist_score = 3000;
1521
1522 /* now compare each segment to the others */
1523 for ( seg1 = segments; seg1 < segment_limit; seg1++ )
1524 {
1525 if ( seg1->dir != axis->major_dir )
1526 continue;
1527
1528 /* search for stems having opposite directions, */
1529 /* with seg1 to the `left' of seg2 */
1530 for ( seg2 = segments; seg2 < segment_limit; seg2++ )
1531 {
1532 FT_Pos pos1 = seg1->pos;
1533 FT_Pos pos2 = seg2->pos;
1534
1535
1536 if ( seg1->dir + seg2->dir == 0 && pos2 > pos1 )
1537 {
1538 /* compute distance between the two segments */
1539 FT_Pos min = seg1->min_coord;
1540 FT_Pos max = seg1->max_coord;
1541 FT_Pos len;
1542
1543
1544 if ( min < seg2->min_coord )
1545 min = seg2->min_coord;
1546
1547 if ( max > seg2->max_coord )
1548 max = seg2->max_coord;
1549
1550 /* compute maximum coordinate difference of the two segments */
1551 /* (this is, how much they overlap) */
1552 len = max - min;
1553 if ( len >= len_threshold )
1554 {
1555 /*
1556 * The score is the sum of two demerits indicating the
1557 * `badness' of a fit, measured along the segments' main axis
1558 * and orthogonal to it, respectively.
1559 *
1560 * o The less overlapping along the main axis, the worse it
1561 * is, causing a larger demerit.
1562 *
1563 * o The nearer the orthogonal distance to a stem width, the
1564 * better it is, causing a smaller demerit. For simplicity,
1565 * however, we only increase the demerit for values that
1566 * exceed the largest stem width.
1567 */
1568
1569 FT_Pos dist = pos2 - pos1;
1570
1571 FT_Pos dist_demerit, score;
1572
1573
1574 if ( max_width )
1575 {
1576 /* distance demerits are based on multiples of `max_width'; */
1577 /* we scale by 1024 for getting more precision */
1578 FT_Pos delta = ( dist << 10 ) / max_width - ( 1 << 10 );
1579
1580
1581 if ( delta > 10000 )
1582 dist_demerit = 32000;
1583 else if ( delta > 0 )
1584 dist_demerit = delta * delta / dist_score;
1585 else
1586 dist_demerit = 0;
1587 }
1588 else
1589 dist_demerit = dist; /* default if no widths available */
1590
1591 score = dist_demerit + len_score / len;
1592
1593 /* and we search for the smallest score */
1594 if ( score < seg1->score )
1595 {
1596 seg1->score = score;
1597 seg1->link = seg2;
1598 }
1599
1600 if ( score < seg2->score )
1601 {
1602 seg2->score = score;
1603 seg2->link = seg1;
1604 }
1605 }
1606 }
1607 }
1608 }
1609
1610 /* now compute the `serif' segments, cf. explanations in `afhints.h' */
1611 for ( seg1 = segments; seg1 < segment_limit; seg1++ )
1612 {
1613 seg2 = seg1->link;
1614
1615 if ( seg2 )
1616 {
1617 if ( seg2->link != seg1 )
1618 {
1619 seg1->link = 0;
1620 seg1->serif = seg2->link;
1621 }
1622 }
1623 }
1624 }
1625
1626
1627 /* Link segments to edges, using feature analysis for selection. */
1628
1629 FT_LOCAL_DEF( FT_Error )
1630 af_latin_hints_compute_edges( AF_GlyphHints hints,
1631 AF_Dimension dim )
1632 {
1633 AF_AxisHints axis = &hints->axis[dim];
1634 FT_Error error = FT_Err_Ok;
1635 FT_Memory memory = hints->memory;
1636 AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
1637
1638 AF_Segment segments = axis->segments;
1639 AF_Segment segment_limit = segments + axis->num_segments;
1640 AF_Segment seg;
1641
1642 #if 0
1643 AF_Direction up_dir;
1644 #endif
1645 FT_Fixed scale;
1646 FT_Pos edge_distance_threshold;
1647 FT_Pos segment_length_threshold;
1648
1649
1650 axis->num_edges = 0;
1651
1652 scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
1653 : hints->y_scale;
1654
1655 #if 0
1656 up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
1657 : AF_DIR_RIGHT;
1658 #endif
1659
1660 /*
1661 * We ignore all segments that are less than 1 pixel in length
1662 * to avoid many problems with serif fonts. We compute the
1663 * corresponding threshold in font units.
1664 */
1665 if ( dim == AF_DIMENSION_HORZ )
1666 segment_length_threshold = FT_DivFix( 64, hints->y_scale );
1667 else
1668 segment_length_threshold = 0;
1669
1670 /*********************************************************************/
1671 /* */
1672 /* We begin by generating a sorted table of edges for the current */
1673 /* direction. To do so, we simply scan each segment and try to find */
1674 /* an edge in our table that corresponds to its position. */
1675 /* */
1676 /* If no edge is found, we create and insert a new edge in the */
1677 /* sorted table. Otherwise, we simply add the segment to the edge's */
1678 /* list which gets processed in the second step to compute the */
1679 /* edge's properties. */
1680 /* */
1681 /* Note that the table of edges is sorted along the segment/edge */
1682 /* position. */
1683 /* */
1684 /*********************************************************************/
1685
1686 /* assure that edge distance threshold is at most 0.25px */
1687 edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
1688 scale );
1689 if ( edge_distance_threshold > 64 / 4 )
1690 edge_distance_threshold = 64 / 4;
1691
1692 edge_distance_threshold = FT_DivFix( edge_distance_threshold,
1693 scale );
1694
1695 for ( seg = segments; seg < segment_limit; seg++ )
1696 {
1697 AF_Edge found = NULL;
1698 FT_Int ee;
1699
1700
1701 if ( seg->height < segment_length_threshold )
1702 continue;
1703
1704 /* A special case for serif edges: If they are smaller than */
1705 /* 1.5 pixels we ignore them. */
1706 if ( seg->serif &&
1707 2 * seg->height < 3 * segment_length_threshold )
1708 continue;
1709
1710 /* look for an edge corresponding to the segment */
1711 for ( ee = 0; ee < axis->num_edges; ee++ )
1712 {
1713 AF_Edge edge = axis->edges + ee;
1714 FT_Pos dist;
1715
1716
1717 dist = seg->pos - edge->fpos;
1718 if ( dist < 0 )
1719 dist = -dist;
1720
1721 if ( dist < edge_distance_threshold && edge->dir == seg->dir )
1722 {
1723 found = edge;
1724 break;
1725 }
1726 }
1727
1728 if ( !found )
1729 {
1730 AF_Edge edge;
1731
1732
1733 /* insert a new edge in the list and */
1734 /* sort according to the position */
1735 error = af_axis_hints_new_edge( axis, seg->pos,
1736 (AF_Direction)seg->dir,
1737 memory, &edge );
1738 if ( error )
1739 goto Exit;
1740
1741 /* add the segment to the new edge's list */
1742 FT_ZERO( edge );
1743
1744 edge->first = seg;
1745 edge->last = seg;
1746 edge->dir = seg->dir;
1747 edge->fpos = seg->pos;
1748 edge->opos = FT_MulFix( seg->pos, scale );
1749 edge->pos = edge->opos;
1750 seg->edge_next = seg;
1751 }
1752 else
1753 {
1754 /* if an edge was found, simply add the segment to the edge's */
1755 /* list */
1756 seg->edge_next = found->first;
1757 found->last->edge_next = seg;
1758 found->last = seg;
1759 }
1760 }
1761
1762
1763 /******************************************************************/
1764 /* */
1765 /* Good, we now compute each edge's properties according to the */
1766 /* segments found on its position. Basically, these are */
1767 /* */
1768 /* - the edge's main direction */
1769 /* - stem edge, serif edge or both (which defaults to stem then) */
1770 /* - rounded edge, straight or both (which defaults to straight) */
1771 /* - link for edge */
1772 /* */
1773 /******************************************************************/
1774
1775 /* first of all, set the `edge' field in each segment -- this is */
1776 /* required in order to compute edge links */
1777
1778 /*
1779 * Note that removing this loop and setting the `edge' field of each
1780 * segment directly in the code above slows down execution speed for
1781 * some reasons on platforms like the Sun.
1782 */
1783 {
1784 AF_Edge edges = axis->edges;
1785 AF_Edge edge_limit = edges + axis->num_edges;
1786 AF_Edge edge;
1787
1788
1789 for ( edge = edges; edge < edge_limit; edge++ )
1790 {
1791 seg = edge->first;
1792 if ( seg )
1793 do
1794 {
1795 seg->edge = edge;
1796 seg = seg->edge_next;
1797
1798 } while ( seg != edge->first );
1799 }
1800
1801 /* now compute each edge properties */
1802 for ( edge = edges; edge < edge_limit; edge++ )
1803 {
1804 FT_Int is_round = 0; /* does it contain round segments? */
1805 FT_Int is_straight = 0; /* does it contain straight segments? */
1806 #if 0
1807 FT_Pos ups = 0; /* number of upwards segments */
1808 FT_Pos downs = 0; /* number of downwards segments */
1809 #endif
1810
1811
1812 seg = edge->first;
1813
1814 do
1815 {
1816 FT_Bool is_serif;
1817
1818
1819 /* check for roundness of segment */
1820 if ( seg->flags & AF_EDGE_ROUND )
1821 is_round++;
1822 else
1823 is_straight++;
1824
1825 #if 0
1826 /* check for segment direction */
1827 if ( seg->dir == up_dir )
1828 ups += seg->max_coord - seg->min_coord;
1829 else
1830 downs += seg->max_coord - seg->min_coord;
1831 #endif
1832
1833 /* check for links -- if seg->serif is set, then seg->link must */
1834 /* be ignored */
1835 is_serif = (FT_Bool)( seg->serif &&
1836 seg->serif->edge &&
1837 seg->serif->edge != edge );
1838
1839 if ( ( seg->link && seg->link->edge != NULL ) || is_serif )
1840 {
1841 AF_Edge edge2;
1842 AF_Segment seg2;
1843
1844
1845 edge2 = edge->link;
1846 seg2 = seg->link;
1847
1848 if ( is_serif )
1849 {
1850 seg2 = seg->serif;
1851 edge2 = edge->serif;
1852 }
1853
1854 if ( edge2 )
1855 {
1856 FT_Pos edge_delta;
1857 FT_Pos seg_delta;
1858
1859
1860 edge_delta = edge->fpos - edge2->fpos;
1861 if ( edge_delta < 0 )
1862 edge_delta = -edge_delta;
1863
1864 seg_delta = seg->pos - seg2->pos;
1865 if ( seg_delta < 0 )
1866 seg_delta = -seg_delta;
1867
1868 if ( seg_delta < edge_delta )
1869 edge2 = seg2->edge;
1870 }
1871 else
1872 edge2 = seg2->edge;
1873
1874 if ( is_serif )
1875 {
1876 edge->serif = edge2;
1877 edge2->flags |= AF_EDGE_SERIF;
1878 }
1879 else
1880 edge->link = edge2;
1881 }
1882
1883 seg = seg->edge_next;
1884
1885 } while ( seg != edge->first );
1886
1887 /* set the round/straight flags */
1888 edge->flags = AF_EDGE_NORMAL;
1889
1890 if ( is_round > 0 && is_round >= is_straight )
1891 edge->flags |= AF_EDGE_ROUND;
1892
1893 #if 0
1894 /* set the edge's main direction */
1895 edge->dir = AF_DIR_NONE;
1896
1897 if ( ups > downs )
1898 edge->dir = (FT_Char)up_dir;
1899
1900 else if ( ups < downs )
1901 edge->dir = (FT_Char)-up_dir;
1902
1903 else if ( ups == downs )
1904 edge->dir = 0; /* both up and down! */
1905 #endif
1906
1907 /* get rid of serifs if link is set */
1908 /* XXX: This gets rid of many unpleasant artefacts! */
1909 /* Example: the `c' in cour.pfa at size 13 */
1910
1911 if ( edge->serif && edge->link )
1912 edge->serif = NULL;
1913 }
1914 }
1915
1916 Exit:
1917 return error;
1918 }
1919
1920
1921 /* Detect segments and edges for given dimension. */
1922
1923 FT_LOCAL_DEF( FT_Error )
1924 af_latin_hints_detect_features( AF_GlyphHints hints,
1925 FT_UInt width_count,
1926 AF_WidthRec* widths,
1927 AF_Dimension dim )
1928 {
1929 FT_Error error;
1930
1931
1932 error = af_latin_hints_compute_segments( hints, dim );
1933 if ( !error )
1934 {
1935 af_latin_hints_link_segments( hints, width_count, widths, dim );
1936
1937 error = af_latin_hints_compute_edges( hints, dim );
1938 }
1939
1940 return error;
1941 }
1942
1943
1944 /* Compute all edges which lie within blue zones. */
1945
1946 static void
1947 af_latin_hints_compute_blue_edges( AF_GlyphHints hints,
1948 AF_LatinMetrics metrics )
1949 {
1950 AF_AxisHints axis = &hints->axis[AF_DIMENSION_VERT];
1951 AF_Edge edge = axis->edges;
1952 AF_Edge edge_limit = edge + axis->num_edges;
1953 AF_LatinAxis latin = &metrics->axis[AF_DIMENSION_VERT];
1954 FT_Fixed scale = latin->scale;
1955
1956
1957 /* compute which blue zones are active, i.e. have their scaled */
1958 /* size < 3/4 pixels */
1959
1960 /* for each horizontal edge search the blue zone which is closest */
1961 for ( ; edge < edge_limit; edge++ )
1962 {
1963 FT_UInt bb;
1964 AF_Width best_blue = NULL;
1965 FT_Bool best_blue_is_neutral = 0;
1966 FT_Pos best_dist; /* initial threshold */
1967
1968
1969 /* compute the initial threshold as a fraction of the EM size */
1970 /* (the value 40 is heuristic) */
1971 best_dist = FT_MulFix( metrics->units_per_em / 40, scale );
1972
1973 /* assure a minimum distance of 0.5px */
1974 if ( best_dist > 64 / 2 )
1975 best_dist = 64 / 2;
1976
1977 for ( bb = 0; bb < latin->blue_count; bb++ )
1978 {
1979 AF_LatinBlue blue = latin->blues + bb;
1980 FT_Bool is_top_blue, is_neutral_blue, is_major_dir;
1981
1982
1983 /* skip inactive blue zones (i.e., those that are too large) */
1984 if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
1985 continue;
1986
1987 /* if it is a top zone, check for right edges (against the major */
1988 /* direction); if it is a bottom zone, check for left edges (in */
1989 /* the major direction) -- this assumes the TrueType convention */
1990 /* for the orientation of contours */
1991 is_top_blue =
1992 (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 );
1993 is_neutral_blue =
1994 (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_NEUTRAL ) != 0);
1995 is_major_dir =
1996 FT_BOOL( edge->dir == axis->major_dir );
1997
1998 /* neutral blue zones are handled for both directions */
1999 if ( is_top_blue ^ is_major_dir || is_neutral_blue )
2000 {
2001 FT_Pos dist;
2002
2003
2004 /* first of all, compare it to the reference position */
2005 dist = edge->fpos - blue->ref.org;
2006 if ( dist < 0 )
2007 dist = -dist;
2008
2009 dist = FT_MulFix( dist, scale );
2010 if ( dist < best_dist )
2011 {
2012 best_dist = dist;
2013 best_blue = &blue->ref;
2014 best_blue_is_neutral = is_neutral_blue;
2015 }
2016
2017 /* now compare it to the overshoot position and check whether */
2018 /* the edge is rounded, and whether the edge is over the */
2019 /* reference position of a top zone, or under the reference */
2020 /* position of a bottom zone (provided we don't have a */
2021 /* neutral blue zone) */
2022 if ( edge->flags & AF_EDGE_ROUND &&
2023 dist != 0 &&
2024 !is_neutral_blue )
2025 {
2026 FT_Bool is_under_ref = FT_BOOL( edge->fpos < blue->ref.org );
2027
2028
2029 if ( is_top_blue ^ is_under_ref )
2030 {
2031 dist = edge->fpos - blue->shoot.org;
2032 if ( dist < 0 )
2033 dist = -dist;
2034
2035 dist = FT_MulFix( dist, scale );
2036 if ( dist < best_dist )
2037 {
2038 best_dist = dist;
2039 best_blue = &blue->shoot;
2040 best_blue_is_neutral = is_neutral_blue;
2041 }
2042 }
2043 }
2044 }
2045 }
2046
2047 if ( best_blue )
2048 {
2049 edge->blue_edge = best_blue;
2050 if ( best_blue_is_neutral )
2051 edge->flags |= AF_EDGE_NEUTRAL;
2052 }
2053 }
2054 }
2055
2056
2057 /* Initalize hinting engine. */
2058
2059 static FT_Error
2060 af_latin_hints_init( AF_GlyphHints hints,
2061 AF_LatinMetrics metrics )
2062 {
2063 FT_Render_Mode mode;
2064 FT_UInt32 scaler_flags, other_flags;
2065 FT_Face face = metrics->root.scaler.face;
2066
2067
2068 af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics );
2069
2070 /*
2071 * correct x_scale and y_scale if needed, since they may have
2072 * been modified by `af_latin_metrics_scale_dim' above
2073 */
2074 hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
2075 hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
2076 hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
2077 hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
2078
2079 /* compute flags depending on render mode, etc. */
2080 mode = metrics->root.scaler.render_mode;
2081
2082 #if 0 /* #ifdef AF_CONFIG_OPTION_USE_WARPER */
2083 if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
2084 metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
2085 #endif
2086
2087 scaler_flags = hints->scaler_flags;
2088 other_flags = 0;
2089
2090 /*
2091 * We snap the width of vertical stems for the monochrome and
2092 * horizontal LCD rendering targets only.
2093 */
2094 if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
2095 other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
2096
2097 /*
2098 * We snap the width of horizontal stems for the monochrome and
2099 * vertical LCD rendering targets only.
2100 */
2101 if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
2102 other_flags |= AF_LATIN_HINTS_VERT_SNAP;
2103
2104 /*
2105 * We adjust stems to full pixels only if we don't use the `light' mode.
2106 */
2107 if ( mode != FT_RENDER_MODE_LIGHT )
2108 other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
2109
2110 if ( mode == FT_RENDER_MODE_MONO )
2111 other_flags |= AF_LATIN_HINTS_MONO;
2112
2113 /*
2114 * In `light' hinting mode we disable horizontal hinting completely.
2115 * We also do it if the face is italic.
2116 *
2117 * However, if warping is enabled (which only works in `light' hinting
2118 * mode), advance widths get adjusted, too.
2119 */
2120 if ( mode == FT_RENDER_MODE_LIGHT ||
2121 ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0 )
2122 scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
2123
2124 #ifdef AF_CONFIG_OPTION_USE_WARPER
2125 /* get (global) warper flag */
2126 if ( !metrics->root.globals->module->warping )
2127 scaler_flags |= AF_SCALER_FLAG_NO_WARPER;
2128 #endif
2129
2130 hints->scaler_flags = scaler_flags;
2131 hints->other_flags = other_flags;
2132
2133 return FT_Err_Ok;
2134 }
2135
2136
2137 /*************************************************************************/
2138 /*************************************************************************/
2139 /***** *****/
2140 /***** L A T I N G L Y P H G R I D - F I T T I N G *****/
2141 /***** *****/
2142 /*************************************************************************/
2143 /*************************************************************************/
2144
2145 /* Snap a given width in scaled coordinates to one of the */
2146 /* current standard widths. */
2147
2148 static FT_Pos
2149 af_latin_snap_width( AF_Width widths,
2150 FT_UInt count,
2151 FT_Pos width )
2152 {
2153 FT_UInt n;
2154 FT_Pos best = 64 + 32 + 2;
2155 FT_Pos reference = width;
2156 FT_Pos scaled;
2157
2158
2159 for ( n = 0; n < count; n++ )
2160 {
2161 FT_Pos w;
2162 FT_Pos dist;
2163
2164
2165 w = widths[n].cur;
2166 dist = width - w;
2167 if ( dist < 0 )
2168 dist = -dist;
2169 if ( dist < best )
2170 {
2171 best = dist;
2172 reference = w;
2173 }
2174 }
2175
2176 scaled = FT_PIX_ROUND( reference );
2177
2178 if ( width >= reference )
2179 {
2180 if ( width < scaled + 48 )
2181 width = reference;
2182 }
2183 else
2184 {
2185 if ( width > scaled - 48 )
2186 width = reference;
2187 }
2188
2189 return width;
2190 }
2191
2192
2193 /* Compute the snapped width of a given stem, ignoring very thin ones. */
2194 /* There is a lot of voodoo in this function; changing the hard-coded */
2195 /* parameters influence the whole hinting process. */
2196
2197 static FT_Pos
2198 af_latin_compute_stem_width( AF_GlyphHints hints,
2199 AF_Dimension dim,
2200 FT_Pos width,
2201 FT_UInt base_flags,
2202 FT_UInt stem_flags )
2203 {
2204 AF_LatinMetrics metrics = (AF_LatinMetrics)hints->metrics;
2205 AF_LatinAxis axis = &metrics->axis[dim];
2206 FT_Pos dist = width;
2207 FT_Int sign = 0;
2208 FT_Int vertical = ( dim == AF_DIMENSION_VERT );
2209
2210
2211 if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ||
2212 axis->extra_light )
2213 return width;
2214
2215 if ( dist < 0 )
2216 {
2217 dist = -width;
2218 sign = 1;
2219 }
2220
2221 if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
2222 ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
2223 {
2224 /* smooth hinting process: very lightly quantize the stem width */
2225
2226 /* leave the widths of serifs alone */
2227 if ( ( stem_flags & AF_EDGE_SERIF ) &&
2228 vertical &&
2229 ( dist < 3 * 64 ) )
2230 goto Done_Width;
2231
2232 else if ( base_flags & AF_EDGE_ROUND )
2233 {
2234 if ( dist < 80 )
2235 dist = 64;
2236 }
2237 else if ( dist < 56 )
2238 dist = 56;
2239
2240 if ( axis->width_count > 0 )
2241 {
2242 FT_Pos delta;
2243
2244
2245 /* compare to standard width */
2246 delta = dist - axis->widths[0].cur;
2247
2248 if ( delta < 0 )
2249 delta = -delta;
2250
2251 if ( delta < 40 )
2252 {
2253 dist = axis->widths[0].cur;
2254 if ( dist < 48 )
2255 dist = 48;
2256
2257 goto Done_Width;
2258 }
2259
2260 if ( dist < 3 * 64 )
2261 {
2262 delta = dist & 63;
2263 dist &= -64;
2264
2265 if ( delta < 10 )
2266 dist += delta;
2267
2268 else if ( delta < 32 )
2269 dist += 10;
2270
2271 else if ( delta < 54 )
2272 dist += 54;
2273
2274 else
2275 dist += delta;
2276 }
2277 else
2278 dist = ( dist + 32 ) & ~63;
2279 }
2280 }
2281 else
2282 {
2283 /* strong hinting process: snap the stem width to integer pixels */
2284
2285 FT_Pos org_dist = dist;
2286
2287
2288 dist = af_latin_snap_width( axis->widths, axis->width_count, dist );
2289
2290 if ( vertical )
2291 {
2292 /* in the case of vertical hinting, always round */
2293 /* the stem heights to integer pixels */
2294
2295 if ( dist >= 64 )
2296 dist = ( dist + 16 ) & ~63;
2297 else
2298 dist = 64;
2299 }
2300 else
2301 {
2302 if ( AF_LATIN_HINTS_DO_MONO( hints ) )
2303 {
2304 /* monochrome horizontal hinting: snap widths to integer pixels */
2305 /* with a different threshold */
2306
2307 if ( dist < 64 )
2308 dist = 64;
2309 else
2310 dist = ( dist + 32 ) & ~63;
2311 }
2312 else
2313 {
2314 /* for horizontal anti-aliased hinting, we adopt a more subtle */
2315 /* approach: we strengthen small stems, round stems whose size */
2316 /* is between 1 and 2 pixels to an integer, otherwise nothing */
2317
2318 if ( dist < 48 )
2319 dist = ( dist + 64 ) >> 1;
2320
2321 else if ( dist < 128 )
2322 {
2323 /* We only round to an integer width if the corresponding */
2324 /* distortion is less than 1/4 pixel. Otherwise this */
2325 /* makes everything worse since the diagonals, which are */
2326 /* not hinted, appear a lot bolder or thinner than the */
2327 /* vertical stems. */
2328
2329 FT_Pos delta;
2330
2331
2332 dist = ( dist + 22 ) & ~63;
2333 delta = dist - org_dist;
2334 if ( delta < 0 )
2335 delta = -delta;
2336
2337 if ( delta >= 16 )
2338 {
2339 dist = org_dist;
2340 if ( dist < 48 )
2341 dist = ( dist + 64 ) >> 1;
2342 }
2343 }
2344 else
2345 /* round otherwise to prevent color fringes in LCD mode */
2346 dist = ( dist + 32 ) & ~63;
2347 }
2348 }
2349 }
2350
2351 Done_Width:
2352 if ( sign )
2353 dist = -dist;
2354
2355 return dist;
2356 }
2357
2358
2359 /* Align one stem edge relative to the previous stem edge. */
2360
2361 static void
2362 af_latin_align_linked_edge( AF_GlyphHints hints,
2363 AF_Dimension dim,
2364 AF_Edge base_edge,
2365 AF_Edge stem_edge )
2366 {
2367 FT_Pos dist = stem_edge->opos - base_edge->opos;
2368
2369 FT_Pos fitted_width = af_latin_compute_stem_width( hints, dim, dist,
2370 base_edge->flags,
2371 stem_edge->flags );
2372
2373
2374 stem_edge->pos = base_edge->pos + fitted_width;
2375
2376 FT_TRACE5(( " LINK: edge %d (opos=%.2f) linked to %.2f,"
2377 " dist was %.2f, now %.2f\n",
2378 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
2379 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 ));
2380 }
2381
2382
2383 /* Shift the coordinates of the `serif' edge by the same amount */
2384 /* as the corresponding `base' edge has been moved already. */
2385
2386 static void
2387 af_latin_align_serif_edge( AF_GlyphHints hints,
2388 AF_Edge base,
2389 AF_Edge serif )
2390 {
2391 FT_UNUSED( hints );
2392
2393 serif->pos = base->pos + ( serif->opos - base->opos );
2394 }
2395
2396
2397 /*************************************************************************/
2398 /*************************************************************************/
2399 /*************************************************************************/
2400 /**** ****/
2401 /**** E D G E H I N T I N G ****/
2402 /**** ****/
2403 /*************************************************************************/
2404 /*************************************************************************/
2405 /*************************************************************************/
2406
2407
2408 /* The main grid-fitting routine. */
2409
2410 static void
2411 af_latin_hint_edges( AF_GlyphHints hints,
2412 AF_Dimension dim )
2413 {
2414 AF_AxisHints axis = &hints->axis[dim];
2415 AF_Edge edges = axis->edges;
2416 AF_Edge edge_limit = edges + axis->num_edges;
2417 FT_PtrDist n_edges;
2418 AF_Edge edge;
2419 AF_Edge anchor = NULL;
2420 FT_Int has_serifs = 0;
2421
2422 #ifdef FT_DEBUG_LEVEL_TRACE
2423 FT_UInt num_actions = 0;
2424 #endif
2425
2426
2427 FT_TRACE5(( "latin %s edge hinting (style `%s')\n",
2428 dim == AF_DIMENSION_VERT ? "horizontal" : "vertical",
2429 af_style_names[hints->metrics->style_class->style] ));
2430
2431 /* we begin by aligning all stems relative to the blue zone */
2432 /* if needed -- that's only for horizontal edges */
2433
2434 if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
2435 {
2436 for ( edge = edges; edge < edge_limit; edge++ )
2437 {
2438 AF_Width blue;
2439 AF_Edge edge1, edge2; /* these edges form the stem to check */
2440
2441
2442 if ( edge->flags & AF_EDGE_DONE )
2443 continue;
2444
2445 edge1 = NULL;
2446 edge2 = edge->link;
2447
2448 /*
2449 * If a stem contains both a neutral and a non-neutral blue zone,
2450 * skip the neutral one. Otherwise, outlines with different
2451 * directions might be incorrectly aligned at the same vertical
2452 * position.
2453 *
2454 * If we have two neutral blue zones, skip one of them.
2455 *
2456 */
2457 if ( edge->blue_edge && edge2 && edge2->blue_edge )
2458 {
2459 FT_Byte neutral = edge->flags & AF_EDGE_NEUTRAL;
2460 FT_Byte neutral2 = edge2->flags & AF_EDGE_NEUTRAL;
2461
2462
2463 if ( neutral2 )
2464 {
2465 edge2->blue_edge = NULL;
2466 edge2->flags &= ~AF_EDGE_NEUTRAL;
2467 }
2468 else if ( neutral )
2469 {
2470 edge->blue_edge = NULL;
2471 edge->flags &= ~AF_EDGE_NEUTRAL;
2472 }
2473 }
2474
2475 blue = edge->blue_edge;
2476 if ( blue )
2477 edge1 = edge;
2478
2479 /* flip edges if the other edge is aligned to a blue zone */
2480 else if ( edge2 && edge2->blue_edge )
2481 {
2482 blue = edge2->blue_edge;
2483 edge1 = edge2;
2484 edge2 = edge;
2485 }
2486
2487 if ( !edge1 )
2488 continue;
2489
2490 #ifdef FT_DEBUG_LEVEL_TRACE
2491 if ( !anchor )
2492 FT_TRACE5(( " BLUE_ANCHOR: edge %d (opos=%.2f) snapped to %.2f,"
2493 " was %.2f (anchor=edge %d)\n",
2494 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2495 edge1->pos / 64.0, edge - edges ));
2496 else
2497 FT_TRACE5(( " BLUE: edge %d (opos=%.2f) snapped to %.2f,"
2498 " was %.2f\n",
2499 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
2500 edge1->pos / 64.0 ));
2501
2502 num_actions++;
2503 #endif
2504
2505 edge1->pos = blue->fit;
2506 edge1->flags |= AF_EDGE_DONE;
2507
2508 if ( edge2 && !edge2->blue_edge )
2509 {
2510 af_latin_align_linked_edge( hints, dim, edge1, edge2 );
2511 edge2->flags |= AF_EDGE_DONE;
2512
2513 #ifdef FT_DEBUG_LEVEL_TRACE
2514 num_actions++;
2515 #endif
2516 }
2517
2518 if ( !anchor )
2519 anchor = edge;
2520 }
2521 }
2522
2523 /* now we align all other stem edges, trying to maintain the */
2524 /* relative order of stems in the glyph */
2525 for ( edge = edges; edge < edge_limit; edge++ )
2526 {
2527 AF_Edge edge2;
2528
2529
2530 if ( edge->flags & AF_EDGE_DONE )
2531 continue;
2532
2533 /* skip all non-stem edges */
2534 edge2 = edge->link;
2535 if ( !edge2 )
2536 {
2537 has_serifs++;
2538 continue;
2539 }
2540
2541 /* now align the stem */
2542
2543 /* this should not happen, but it's better to be safe */
2544 if ( edge2->blue_edge )
2545 {
2546 FT_TRACE5(( " ASSERTION FAILED for edge %d\n", edge2 - edges ));
2547
2548 af_latin_align_linked_edge( hints, dim, edge2, edge );
2549 edge->flags |= AF_EDGE_DONE;
2550
2551 #ifdef FT_DEBUG_LEVEL_TRACE
2552 num_actions++;
2553 #endif
2554 continue;
2555 }
2556
2557 if ( !anchor )
2558 {
2559 /* if we reach this if clause, no stem has been aligned yet */
2560
2561 FT_Pos org_len, org_center, cur_len;
2562 FT_Pos cur_pos1, error1, error2, u_off, d_off;
2563
2564
2565 org_len = edge2->opos - edge->opos;
2566 cur_len = af_latin_compute_stem_width( hints, dim, org_len,
2567 edge->flags,
2568 edge2->flags );
2569
2570 /* some voodoo to specially round edges for small stem widths; */
2571 /* the idea is to align the center of a stem, then shifting */
2572 /* the stem edges to suitable positions */
2573 if ( cur_len <= 64 )
2574 {
2575 /* width <= 1px */
2576 u_off = 32;
2577 d_off = 32;
2578 }
2579 else
2580 {
2581 /* 1px < width < 1.5px */
2582 u_off = 38;
2583 d_off = 26;
2584 }
2585
2586 if ( cur_len < 96 )
2587 {
2588 org_center = edge->opos + ( org_len >> 1 );
2589 cur_pos1 = FT_PIX_ROUND( org_center );
2590
2591 error1 = org_center - ( cur_pos1 - u_off );
2592 if ( error1 < 0 )
2593 error1 = -error1;
2594
2595 error2 = org_center - ( cur_pos1 + d_off );
2596 if ( error2 < 0 )
2597 error2 = -error2;
2598
2599 if ( error1 < error2 )
2600 cur_pos1 -= u_off;
2601 else
2602 cur_pos1 += d_off;
2603
2604 edge->pos = cur_pos1 - cur_len / 2;
2605 edge2->pos = edge->pos + cur_len;
2606 }
2607 else
2608 edge->pos = FT_PIX_ROUND( edge->opos );
2609
2610 anchor = edge;
2611 edge->flags |= AF_EDGE_DONE;
2612
2613 FT_TRACE5(( " ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
2614 " snapped to %.2f and %.2f\n",
2615 edge - edges, edge->opos / 64.0,
2616 edge2 - edges, edge2->opos / 64.0,
2617 edge->pos / 64.0, edge2->pos / 64.0 ));
2618
2619 af_latin_align_linked_edge( hints, dim, edge, edge2 );
2620
2621 #ifdef FT_DEBUG_LEVEL_TRACE
2622 num_actions += 2;
2623 #endif
2624 }
2625 else
2626 {
2627 FT_Pos org_pos, org_len, org_center, cur_len;
2628 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
2629
2630
2631 org_pos = anchor->pos + ( edge->opos - anchor->opos );
2632 org_len = edge2->opos - edge->opos;
2633 org_center = org_pos + ( org_len >> 1 );
2634
2635 cur_len = af_latin_compute_stem_width( hints, dim, org_len,
2636 edge->flags,
2637 edge2->flags );
2638
2639 if ( edge2->flags & AF_EDGE_DONE )
2640 {
2641 FT_TRACE5(( " ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
2642 edge - edges, edge->pos / 64.0,
2643 ( edge2->pos - cur_len ) / 64.0 ));
2644
2645 edge->pos = edge2->pos - cur_len;
2646 }
2647
2648 else if ( cur_len < 96 )
2649 {
2650 FT_Pos u_off, d_off;
2651
2652
2653 cur_pos1 = FT_PIX_ROUND( org_center );
2654
2655 if ( cur_len <= 64 )
2656 {
2657 u_off = 32;
2658 d_off = 32;
2659 }
2660 else
2661 {
2662 u_off = 38;
2663 d_off = 26;
2664 }
2665
2666 delta1 = org_center - ( cur_pos1 - u_off );
2667 if ( delta1 < 0 )
2668 delta1 = -delta1;
2669
2670 delta2 = org_center - ( cur_pos1 + d_off );
2671 if ( delta2 < 0 )
2672 delta2 = -delta2;
2673
2674 if ( delta1 < delta2 )
2675 cur_pos1 -= u_off;
2676 else
2677 cur_pos1 += d_off;
2678
2679 edge->pos = cur_pos1 - cur_len / 2;
2680 edge2->pos = cur_pos1 + cur_len / 2;
2681
2682 FT_TRACE5(( " STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2683 " snapped to %.2f and %.2f\n",
2684 edge - edges, edge->opos / 64.0,
2685 edge2 - edges, edge2->opos / 64.0,
2686 edge->pos / 64.0, edge2->pos / 64.0 ));
2687 }
2688
2689 else
2690 {
2691 org_pos = anchor->pos + ( edge->opos - anchor->opos );
2692 org_len = edge2->opos - edge->opos;
2693 org_center = org_pos + ( org_len >> 1 );
2694
2695 cur_len = af_latin_compute_stem_width( hints, dim, org_len,
2696 edge->flags,
2697 edge2->flags );
2698
2699 cur_pos1 = FT_PIX_ROUND( org_pos );
2700 delta1 = cur_pos1 + ( cur_len >> 1 ) - org_center;
2701 if ( delta1 < 0 )
2702 delta1 = -delta1;
2703
2704 cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len;
2705 delta2 = cur_pos2 + ( cur_len >> 1 ) - org_center;
2706 if ( delta2 < 0 )
2707 delta2 = -delta2;
2708
2709 edge->pos = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2;
2710 edge2->pos = edge->pos + cur_len;
2711
2712 FT_TRACE5(( " STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2713 " snapped to %.2f and %.2f\n",
2714 edge - edges, edge->opos / 64.0,
2715 edge2 - edges, edge2->opos / 64.0,
2716 edge->pos / 64.0, edge2->pos / 64.0 ));
2717 }
2718
2719 #ifdef FT_DEBUG_LEVEL_TRACE
2720 num_actions++;
2721 #endif
2722
2723 edge->flags |= AF_EDGE_DONE;
2724 edge2->flags |= AF_EDGE_DONE;
2725
2726 if ( edge > edges && edge->pos < edge[-1].pos )
2727 {
2728 #ifdef FT_DEBUG_LEVEL_TRACE
2729 FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2730 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0 ));
2731
2732 num_actions++;
2733 #endif
2734
2735 edge->pos = edge[-1].pos;
2736 }
2737 }
2738 }
2739
2740 /* make sure that lowercase m's maintain their symmetry */
2741
2742 /* In general, lowercase m's have six vertical edges if they are sans */
2743 /* serif, or twelve if they are with serifs. This implementation is */
2744 /* based on that assumption, and seems to work very well with most */
2745 /* faces. However, if for a certain face this assumption is not */
2746 /* true, the m is just rendered like before. In addition, any stem */
2747 /* correction will only be applied to symmetrical glyphs (even if the */
2748 /* glyph is not an m), so the potential for unwanted distortion is */
2749 /* relatively low. */
2750
2751 /* We don't handle horizontal edges since we can't easily assure that */
2752 /* the third (lowest) stem aligns with the base line; it might end up */
2753 /* one pixel higher or lower. */
2754
2755 n_edges = edge_limit - edges;
2756 if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
2757 {
2758 AF_Edge edge1, edge2, edge3;
2759 FT_Pos dist1, dist2, span, delta;
2760
2761
2762 if ( n_edges == 6 )
2763 {
2764 edge1 = edges;
2765 edge2 = edges + 2;
2766 edge3 = edges + 4;
2767 }
2768 else
2769 {
2770 edge1 = edges + 1;
2771 edge2 = edges + 5;
2772 edge3 = edges + 9;
2773 }
2774
2775 dist1 = edge2->opos - edge1->opos;
2776 dist2 = edge3->opos - edge2->opos;
2777
2778 span = dist1 - dist2;
2779 if ( span < 0 )
2780 span = -span;
2781
2782 if ( span < 8 )
2783 {
2784 delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
2785 edge3->pos -= delta;
2786 if ( edge3->link )
2787 edge3->link->pos -= delta;
2788
2789 /* move the serifs along with the stem */
2790 if ( n_edges == 12 )
2791 {
2792 ( edges + 8 )->pos -= delta;
2793 ( edges + 11 )->pos -= delta;
2794 }
2795
2796 edge3->flags |= AF_EDGE_DONE;
2797 if ( edge3->link )
2798 edge3->link->flags |= AF_EDGE_DONE;
2799 }
2800 }
2801
2802 if ( has_serifs || !anchor )
2803 {
2804 /*
2805 * now hint the remaining edges (serifs and single) in order
2806 * to complete our processing
2807 */
2808 for ( edge = edges; edge < edge_limit; edge++ )
2809 {
2810 FT_Pos delta;
2811
2812
2813 if ( edge->flags & AF_EDGE_DONE )
2814 continue;
2815
2816 delta = 1000;
2817
2818 if ( edge->serif )
2819 {
2820 delta = edge->serif->opos - edge->opos;
2821 if ( delta < 0 )
2822 delta = -delta;
2823 }
2824
2825 if ( delta < 64 + 16 )
2826 {
2827 af_latin_align_serif_edge( hints, edge->serif, edge );
2828 FT_TRACE5(( " SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2829 " aligned to %.2f\n",
2830 edge - edges, edge->opos / 64.0,
2831 edge->serif - edges, edge->serif->opos / 64.0,
2832 edge->pos / 64.0 ));
2833 }
2834 else if ( !anchor )
2835 {
2836 edge->pos = FT_PIX_ROUND( edge->opos );
2837 anchor = edge;
2838 FT_TRACE5(( " SERIF_ANCHOR: edge %d (opos=%.2f)"
2839 " snapped to %.2f\n",
2840 edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
2841 }
2842 else
2843 {
2844 AF_Edge before, after;
2845
2846
2847 for ( before = edge - 1; before >= edges; before-- )
2848 if ( before->flags & AF_EDGE_DONE )
2849 break;
2850
2851 for ( after = edge + 1; after < edge_limit; after++ )
2852 if ( after->flags & AF_EDGE_DONE )
2853 break;
2854
2855 if ( before >= edges && before < edge &&
2856 after < edge_limit && after > edge )
2857 {
2858 if ( after->opos == before->opos )
2859 edge->pos = before->pos;
2860 else
2861 edge->pos = before->pos +
2862 FT_MulDiv( edge->opos - before->opos,
2863 after->pos - before->pos,
2864 after->opos - before->opos );
2865
2866 FT_TRACE5(( " SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2867 " from %d (opos=%.2f)\n",
2868 edge - edges, edge->opos / 64.0,
2869 edge->pos / 64.0,
2870 before - edges, before->opos / 64.0 ));
2871 }
2872 else
2873 {
2874 edge->pos = anchor->pos +
2875 ( ( edge->opos - anchor->opos + 16 ) & ~31 );
2876 FT_TRACE5(( " SERIF_LINK2: edge %d (opos=%.2f)"
2877 " snapped to %.2f\n",
2878 edge - edges, edge->opos / 64.0, edge->pos / 64.0 ));
2879 }
2880 }
2881
2882 #ifdef FT_DEBUG_LEVEL_TRACE
2883 num_actions++;
2884 #endif
2885 edge->flags |= AF_EDGE_DONE;
2886
2887 if ( edge > edges && edge->pos < edge[-1].pos )
2888 {
2889 #ifdef FT_DEBUG_LEVEL_TRACE
2890 FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2891 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0 ));
2892
2893 num_actions++;
2894 #endif
2895 edge->pos = edge[-1].pos;
2896 }
2897
2898 if ( edge + 1 < edge_limit &&
2899 edge[1].flags & AF_EDGE_DONE &&
2900 edge->pos > edge[1].pos )
2901 {
2902 #ifdef FT_DEBUG_LEVEL_TRACE
2903 FT_TRACE5(( " BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2904 edge - edges, edge->pos / 64.0, edge[1].pos / 64.0 ));
2905
2906 num_actions++;
2907 #endif
2908
2909 edge->pos = edge[1].pos;
2910 }
2911 }
2912 }
2913
2914 #ifdef FT_DEBUG_LEVEL_TRACE
2915 if ( !num_actions )
2916 FT_TRACE5(( " (none)\n" ));
2917 FT_TRACE5(( "\n" ));
2918 #endif
2919 }
2920
2921
2922 /* Apply the complete hinting algorithm to a latin glyph. */
2923
2924 static FT_Error
2925 af_latin_hints_apply( FT_UInt glyph_index,
2926 AF_GlyphHints hints,
2927 FT_Outline* outline,
2928 AF_LatinMetrics metrics )
2929 {
2930 FT_Error error;
2931 int dim;
2932
2933 AF_LatinAxis axis;
2934
2935
2936 error = af_glyph_hints_reload( hints, outline );
2937 if ( error )
2938 goto Exit;
2939
2940 /* analyze glyph outline */
2941 #ifdef AF_CONFIG_OPTION_USE_WARPER
2942 if ( ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT &&
2943 AF_HINTS_DO_WARP( hints ) ) ||
2944 AF_HINTS_DO_HORIZONTAL( hints ) )
2945 #else
2946 if ( AF_HINTS_DO_HORIZONTAL( hints ) )
2947 #endif
2948 {
2949 axis = &metrics->axis[AF_DIMENSION_HORZ];
2950 error = af_latin_hints_detect_features( hints,
2951 axis->width_count,
2952 axis->widths,
2953 AF_DIMENSION_HORZ );
2954 if ( error )
2955 goto Exit;
2956 }
2957
2958 if ( AF_HINTS_DO_VERTICAL( hints ) )
2959 {
2960 axis = &metrics->axis[AF_DIMENSION_VERT];
2961 error = af_latin_hints_detect_features( hints,
2962 axis->width_count,
2963 axis->widths,
2964 AF_DIMENSION_VERT );
2965 if ( error )
2966 goto Exit;
2967
2968 /* apply blue zones to base characters only */
2969 if ( !( metrics->root.globals->glyph_styles[glyph_index] & AF_NONBASE ) )
2970 af_latin_hints_compute_blue_edges( hints, metrics );
2971 }
2972
2973 /* grid-fit the outline */
2974 for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
2975 {
2976 #ifdef AF_CONFIG_OPTION_USE_WARPER
2977 if ( dim == AF_DIMENSION_HORZ &&
2978 metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT &&
2979 AF_HINTS_DO_WARP( hints ) )
2980 {
2981 AF_WarperRec warper;
2982 FT_Fixed scale;
2983 FT_Pos delta;
2984
2985
2986 af_warper_compute( &warper, hints, (AF_Dimension)dim,
2987 &scale, &delta );
2988 af_glyph_hints_scale_dim( hints, (AF_Dimension)dim,
2989 scale, delta );
2990 continue;
2991 }
2992 #endif /* AF_CONFIG_OPTION_USE_WARPER */
2993
2994 if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
2995 ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
2996 {
2997 af_latin_hint_edges( hints, (AF_Dimension)dim );
2998 af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
2999 af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
3000 af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
3001 }
3002 }
3003
3004 af_glyph_hints_save( hints, outline );
3005
3006 Exit:
3007 return error;
3008 }
3009
3010
3011 /*************************************************************************/
3012 /*************************************************************************/
3013 /***** *****/
3014 /***** L A T I N S C R I P T C L A S S *****/
3015 /***** *****/
3016 /*************************************************************************/
3017 /*************************************************************************/
3018
3019
3020 AF_DEFINE_WRITING_SYSTEM_CLASS(
3021 af_latin_writing_system_class,
3022
3023 AF_WRITING_SYSTEM_LATIN,
3024
3025 sizeof ( AF_LatinMetricsRec ),
3026
3027 (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init,
3028 (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale,
3029 (AF_WritingSystem_DoneMetricsFunc) NULL,
3030 (AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths,
3031
3032 (AF_WritingSystem_InitHintsFunc) af_latin_hints_init,
3033 (AF_WritingSystem_ApplyHintsFunc) af_latin_hints_apply
3034 )
3035
3036
3037 /* END */