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