Visualization Library 2.0.0-b5

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

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