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