c3497091dd6daaf63d8912c264d3b08dcde3c98b
[framework/graphics/freetype.git] / src / autofit / afhints.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  afhints.c                                                              */
4 /*                                                                         */
5 /*    Auto-fitter hinting routines (body).                                 */
6 /*                                                                         */
7 /*  Copyright 2003, 2004, 2005, 2006, 2007, 2009, 2010 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 "afhints.h"
20 #include "aferrors.h"
21 #include FT_INTERNAL_CALC_H
22
23
24   FT_LOCAL_DEF( FT_Error )
25   af_axis_hints_new_segment( AF_AxisHints  axis,
26                              FT_Memory     memory,
27                              AF_Segment   *asegment )
28   {
29     FT_Error    error   = AF_Err_Ok;
30     AF_Segment  segment = NULL;
31
32
33     if ( axis->num_segments >= axis->max_segments )
34     {
35       FT_Int  old_max = axis->max_segments;
36       FT_Int  new_max = old_max;
37       FT_Int  big_max = (FT_Int)( FT_INT_MAX / sizeof ( *segment ) );
38
39
40       if ( old_max >= big_max )
41       {
42         error = AF_Err_Out_Of_Memory;
43         goto Exit;
44       }
45
46       new_max += ( new_max >> 2 ) + 4;
47       if ( new_max < old_max || new_max > big_max )
48         new_max = big_max;
49
50       if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) )
51         goto Exit;
52
53       axis->max_segments = new_max;
54     }
55
56     segment = axis->segments + axis->num_segments++;
57
58   Exit:
59     *asegment = segment;
60     return error;
61   }
62
63
64   FT_LOCAL( FT_Error )
65   af_axis_hints_new_edge( AF_AxisHints  axis,
66                           FT_Int        fpos,
67                           AF_Direction  dir,
68                           FT_Memory     memory,
69                           AF_Edge      *aedge )
70   {
71     FT_Error  error = AF_Err_Ok;
72     AF_Edge   edge  = NULL;
73     AF_Edge   edges;
74
75
76     if ( axis->num_edges >= axis->max_edges )
77     {
78       FT_Int  old_max = axis->max_edges;
79       FT_Int  new_max = old_max;
80       FT_Int  big_max = (FT_Int)( FT_INT_MAX / sizeof ( *edge ) );
81
82
83       if ( old_max >= big_max )
84       {
85         error = AF_Err_Out_Of_Memory;
86         goto Exit;
87       }
88
89       new_max += ( new_max >> 2 ) + 4;
90       if ( new_max < old_max || new_max > big_max )
91         new_max = big_max;
92
93       if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) )
94         goto Exit;
95
96       axis->max_edges = new_max;
97     }
98
99     edges = axis->edges;
100     edge  = edges + axis->num_edges;
101
102     while ( edge > edges )
103     {
104       if ( edge[-1].fpos < fpos )
105         break;
106
107       /* we want the edge with same position and minor direction */
108       /* to appear before those in the major one in the list     */
109       if ( edge[-1].fpos == fpos && dir == axis->major_dir )
110         break;
111
112       edge[0] = edge[-1];
113       edge--;
114     }
115
116     axis->num_edges++;
117
118     FT_ZERO( edge );
119     edge->fpos = (FT_Short)fpos;
120     edge->dir  = (FT_Char)dir;
121
122   Exit:
123     *aedge = edge;
124     return error;
125   }
126
127
128 #ifdef AF_DEBUG
129
130 #include FT_CONFIG_STANDARD_LIBRARY_H
131
132   static const char*
133   af_dir_str( AF_Direction  dir )
134   {
135     const char*  result;
136
137
138     switch ( dir )
139     {
140     case AF_DIR_UP:
141       result = "up";
142       break;
143     case AF_DIR_DOWN:
144       result = "down";
145       break;
146     case AF_DIR_LEFT:
147       result = "left";
148       break;
149     case AF_DIR_RIGHT:
150       result = "right";
151       break;
152     default:
153       result = "none";
154     }
155
156     return result;
157   }
158
159
160 #define AF_INDEX_NUM( ptr, base )  ( (ptr) ? ( (ptr) - (base) ) : -1 )
161
162
163   void
164   af_glyph_hints_dump_points( AF_GlyphHints  hints )
165   {
166     AF_Point  points = hints->points;
167     AF_Point  limit  = points + hints->num_points;
168     AF_Point  point;
169
170
171     printf( "Table of points:\n" );
172     printf(   "  [ index |  xorg |  yorg |  xscale |  yscale "
173               "|  xfit  |  yfit  |  flags ]\n" );
174
175     for ( point = points; point < limit; point++ )
176     {
177       printf( "  [ %5d | %5d | %5d | %-5.2f | %-5.2f "
178               "| %-5.2f | %-5.2f | %c%c%c%c%c%c ]\n",
179               point - points,
180               point->fx,
181               point->fy,
182               point->ox/64.0,
183               point->oy/64.0,
184               point->x/64.0,
185               point->y/64.0,
186               ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ? 'w' : ' ',
187               ( point->flags & AF_FLAG_INFLECTION )         ? 'i' : ' ',
188               ( point->flags & AF_FLAG_EXTREMA_X )          ? '<' : ' ',
189               ( point->flags & AF_FLAG_EXTREMA_Y )          ? 'v' : ' ',
190               ( point->flags & AF_FLAG_ROUND_X )            ? '(' : ' ',
191               ( point->flags & AF_FLAG_ROUND_Y )            ? 'u' : ' ');
192     }
193     printf( "\n" );
194   }
195
196
197   static const char*
198   af_edge_flags_to_string( AF_Edge_Flags  flags )
199   {
200     static char  temp[32];
201     int          pos = 0;
202
203
204     if ( flags & AF_EDGE_ROUND )
205     {
206       ft_memcpy( temp + pos, "round", 5 );
207       pos += 5;
208     }
209     if ( flags & AF_EDGE_SERIF )
210     {
211       if ( pos > 0 )
212         temp[pos++] = ' ';
213       ft_memcpy( temp + pos, "serif", 5 );
214       pos += 5;
215     }
216     if ( pos == 0 )
217       return "normal";
218
219     temp[pos] = 0;
220
221     return temp;
222   }
223
224
225   /* A function to dump the array of linked segments. */
226   void
227   af_glyph_hints_dump_segments( AF_GlyphHints  hints )
228   {
229     FT_Int  dimension;
230
231
232     for ( dimension = 1; dimension >= 0; dimension-- )
233     {
234       AF_AxisHints  axis     = &hints->axis[dimension];
235       AF_Segment    segments = axis->segments;
236       AF_Segment    limit    = segments + axis->num_segments;
237       AF_Segment    seg;
238
239
240       printf ( "Table of %s segments:\n",
241                dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" );
242       printf ( "  [ index |  pos  |  dir  | link | serif |"
243                " height  | extra | flags    ]\n" );
244
245       for ( seg = segments; seg < limit; seg++ )
246       {
247         printf ( "  [ %5d | %5.2g | %5s | %4d | %5d | %5d | %5d | %s ]\n",
248                  seg - segments,
249                  dimension == AF_DIMENSION_HORZ ? (int)seg->first->ox / 64.0
250                                                 : (int)seg->first->oy / 64.0,
251                  af_dir_str( (AF_Direction)seg->dir ),
252                  AF_INDEX_NUM( seg->link, segments ),
253                  AF_INDEX_NUM( seg->serif, segments ),
254                  seg->height,
255                  seg->height - ( seg->max_coord - seg->min_coord ),
256                  af_edge_flags_to_string( seg->flags ) );
257       }
258       printf( "\n" );
259     }
260   }
261
262
263   void
264   af_glyph_hints_dump_edges( AF_GlyphHints  hints )
265   {
266     FT_Int  dimension;
267
268
269     for ( dimension = 1; dimension >= 0; dimension-- )
270     {
271       AF_AxisHints  axis  = &hints->axis[dimension];
272       AF_Edge       edges = axis->edges;
273       AF_Edge       limit = edges + axis->num_edges;
274       AF_Edge       edge;
275
276
277       /*
278        *  note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
279        *        since they have constant a X coordinate.
280        */
281       printf ( "Table of %s edges:\n",
282                dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" );
283       printf ( "  [ index |  pos  |  dir  | link |"
284                " serif | blue | opos  |  pos  | flags   ]\n" );
285
286       for ( edge = edges; edge < limit; edge++ )
287       {
288         printf ( "  [ %5d | %5.2g | %5s | %4d |"
289                  " %5d |   %c  | %5.2f | %5.2f | %s ]\n",
290                  edge - edges,
291                  (int)edge->opos / 64.0,
292                  af_dir_str( (AF_Direction)edge->dir ),
293                  AF_INDEX_NUM( edge->link, edges ),
294                  AF_INDEX_NUM( edge->serif, edges ),
295                  edge->blue_edge ? 'y' : 'n',
296                  edge->opos / 64.0,
297                  edge->pos / 64.0,
298                  af_edge_flags_to_string( edge->flags ) );
299       }
300       printf( "\n" );
301     }
302   }
303
304 #else /* !AF_DEBUG */
305
306   /* these empty stubs are only used to link the `ftgrid' test program */
307   /* when debugging is disabled                                        */
308
309   void
310   af_glyph_hints_dump_points( AF_GlyphHints  hints )
311   {
312     FT_UNUSED( hints );
313   }
314
315
316   void
317   af_glyph_hints_dump_segments( AF_GlyphHints  hints )
318   {
319     FT_UNUSED( hints );
320   }
321
322
323   void
324   af_glyph_hints_dump_edges( AF_GlyphHints  hints )
325   {
326     FT_UNUSED( hints );
327   }
328
329 #endif /* !AF_DEBUG */
330
331
332   /* compute the direction value of a given vector */
333   FT_LOCAL_DEF( AF_Direction )
334   af_direction_compute( FT_Pos  dx,
335                         FT_Pos  dy )
336   {
337     FT_Pos        ll, ss;  /* long and short arm lengths */
338     AF_Direction  dir;     /* candidate direction        */
339
340
341     if ( dy >= dx )
342     {
343       if ( dy >= -dx )
344       {
345         dir = AF_DIR_UP;
346         ll  = dy;
347         ss  = dx;
348       }
349       else
350       {
351         dir = AF_DIR_LEFT;
352         ll  = -dx;
353         ss  = dy;
354       }
355     }
356     else /* dy < dx */
357     {
358       if ( dy >= -dx )
359       {
360         dir = AF_DIR_RIGHT;
361         ll  = dx;
362         ss  = dy;
363       }
364       else
365       {
366         dir = AF_DIR_DOWN;
367         ll  = dy;
368         ss  = dx;
369       }
370     }
371
372     ss *= 14;
373     if ( FT_ABS( ll ) <= FT_ABS( ss ) )
374       dir = AF_DIR_NONE;
375
376     return dir;
377   }
378
379
380   FT_LOCAL_DEF( void )
381   af_glyph_hints_init( AF_GlyphHints  hints,
382                        FT_Memory      memory )
383   {
384     FT_ZERO( hints );
385     hints->memory = memory;
386   }
387
388
389   FT_LOCAL_DEF( void )
390   af_glyph_hints_done( AF_GlyphHints  hints )
391   {
392     if ( hints && hints->memory )
393     {
394       FT_Memory  memory = hints->memory;
395       int        dim;
396
397
398       /*
399        *  note that we don't need to free the segment and edge
400        *  buffers, since they are really within the hints->points array
401        */
402       for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
403       {
404         AF_AxisHints  axis = &hints->axis[dim];
405
406
407         axis->num_segments = 0;
408         axis->max_segments = 0;
409         FT_FREE( axis->segments );
410
411         axis->num_edges    = 0;
412         axis->max_edges    = 0;
413         FT_FREE( axis->edges );
414       }
415
416       FT_FREE( hints->contours );
417       hints->max_contours = 0;
418       hints->num_contours = 0;
419
420       FT_FREE( hints->points );
421       hints->num_points = 0;
422       hints->max_points = 0;
423
424       hints->memory = NULL;
425     }
426   }
427
428
429   FT_LOCAL_DEF( void )
430   af_glyph_hints_rescale( AF_GlyphHints     hints,
431                           AF_ScriptMetrics  metrics )
432   {
433     hints->metrics      = metrics;
434     hints->scaler_flags = metrics->scaler.flags;
435   }
436
437
438   FT_LOCAL_DEF( FT_Error )
439   af_glyph_hints_reload( AF_GlyphHints  hints,
440                          FT_Outline*    outline )
441   {
442     FT_Error   error   = AF_Err_Ok;
443     AF_Point   points;
444     FT_UInt    old_max, new_max;
445     FT_Fixed   x_scale = hints->x_scale;
446     FT_Fixed   y_scale = hints->y_scale;
447     FT_Pos     x_delta = hints->x_delta;
448     FT_Pos     y_delta = hints->y_delta;
449     FT_Memory  memory  = hints->memory;
450
451
452     hints->num_points   = 0;
453     hints->num_contours = 0;
454
455     hints->axis[0].num_segments = 0;
456     hints->axis[0].num_edges    = 0;
457     hints->axis[1].num_segments = 0;
458     hints->axis[1].num_edges    = 0;
459
460     /* first of all, reallocate the contours array when necessary */
461     new_max = (FT_UInt)outline->n_contours;
462     old_max = hints->max_contours;
463     if ( new_max > old_max )
464     {
465       new_max = ( new_max + 3 ) & ~3;
466
467       if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
468         goto Exit;
469
470       hints->max_contours = new_max;
471     }
472
473     /*
474      *  then reallocate the points arrays if necessary --
475      *  note that we reserve two additional point positions, used to
476      *  hint metrics appropriately
477      */
478     new_max = (FT_UInt)( outline->n_points + 2 );
479     old_max = hints->max_points;
480     if ( new_max > old_max )
481     {
482       new_max = ( new_max + 2 + 7 ) & ~7;
483
484       if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) )
485         goto Exit;
486
487       hints->max_points = new_max;
488     }
489
490     hints->num_points   = outline->n_points;
491     hints->num_contours = outline->n_contours;
492
493     /* We can't rely on the value of `FT_Outline.flags' to know the fill   */
494     /* direction used for a glyph, given that some fonts are broken (e.g., */
495     /* the Arphic ones).  We thus recompute it each time we need to.       */
496     /*                                                                     */
497     hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP;
498     hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT;
499
500     if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
501     {
502       hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN;
503       hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT;
504     }
505
506     hints->x_scale = x_scale;
507     hints->y_scale = y_scale;
508     hints->x_delta = x_delta;
509     hints->y_delta = y_delta;
510
511     hints->xmin_delta = 0;
512     hints->xmax_delta = 0;
513
514     points = hints->points;
515     if ( hints->num_points == 0 )
516       goto Exit;
517
518     {
519       AF_Point  point;
520       AF_Point  point_limit = points + hints->num_points;
521
522
523       /* compute coordinates & Bezier flags, next and prev */
524       {
525         FT_Vector*  vec           = outline->points;
526         char*       tag           = outline->tags;
527         AF_Point    end           = points + outline->contours[0];
528         AF_Point    prev          = end;
529         FT_Int      contour_index = 0;
530
531
532         for ( point = points; point < point_limit; point++, vec++, tag++ )
533         {
534           point->fx = (FT_Short)vec->x;
535           point->fy = (FT_Short)vec->y;
536           point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
537           point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta;
538
539           switch ( FT_CURVE_TAG( *tag ) )
540           {
541           case FT_CURVE_TAG_CONIC:
542             point->flags = AF_FLAG_CONIC;
543             break;
544           case FT_CURVE_TAG_CUBIC:
545             point->flags = AF_FLAG_CUBIC;
546             break;
547           default:
548             point->flags = 0;
549           }
550
551           point->prev = prev;
552           prev->next  = point;
553           prev        = point;
554
555           if ( point == end )
556           {
557             if ( ++contour_index < outline->n_contours )
558             {
559               end  = points + outline->contours[contour_index];
560               prev = end;
561             }
562           }
563         }
564       }
565
566       /* set-up the contours array */
567       {
568         AF_Point*  contour       = hints->contours;
569         AF_Point*  contour_limit = contour + hints->num_contours;
570         short*     end           = outline->contours;
571         short      idx           = 0;
572
573
574         for ( ; contour < contour_limit; contour++, end++ )
575         {
576           contour[0] = points + idx;
577           idx        = (short)( end[0] + 1 );
578         }
579       }
580
581       /* compute directions of in & out vectors */
582       {
583         AF_Point      first  = points;
584         AF_Point      prev   = NULL;
585         FT_Pos        in_x   = 0;
586         FT_Pos        in_y   = 0;
587         AF_Direction  in_dir = AF_DIR_NONE;
588
589
590         for ( point = points; point < point_limit; point++ )
591         {
592           AF_Point  next;
593           FT_Pos    out_x, out_y;
594
595
596           if ( point == first )
597           {
598             prev   = first->prev;
599             in_x   = first->fx - prev->fx;
600             in_y   = first->fy - prev->fy;
601             in_dir = af_direction_compute( in_x, in_y );
602             first  = prev + 1;
603           }
604
605           point->in_dir = (FT_Char)in_dir;
606
607           next  = point->next;
608           out_x = next->fx - point->fx;
609           out_y = next->fy - point->fy;
610
611           in_dir         = af_direction_compute( out_x, out_y );
612           point->out_dir = (FT_Char)in_dir;
613
614           if ( point->flags & ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) )
615           {
616           Is_Weak_Point:
617             point->flags |= AF_FLAG_WEAK_INTERPOLATION;
618           }
619           else if ( point->out_dir == point->in_dir )
620           {
621             if ( point->out_dir != AF_DIR_NONE )
622               goto Is_Weak_Point;
623
624             if ( ft_corner_is_flat( in_x, in_y, out_x, out_y ) )
625               goto Is_Weak_Point;
626           }
627           else if ( point->in_dir == -point->out_dir )
628             goto Is_Weak_Point;
629
630           in_x = out_x;
631           in_y = out_y;
632           prev = point;
633         }
634       }
635     }
636
637   Exit:
638     return error;
639   }
640
641
642   FT_LOCAL_DEF( void )
643   af_glyph_hints_save( AF_GlyphHints  hints,
644                        FT_Outline*    outline )
645   {
646     AF_Point    point = hints->points;
647     AF_Point    limit = point + hints->num_points;
648     FT_Vector*  vec   = outline->points;
649     char*       tag   = outline->tags;
650
651
652     for ( ; point < limit; point++, vec++, tag++ )
653     {
654       vec->x = point->x;
655       vec->y = point->y;
656
657       if ( point->flags & AF_FLAG_CONIC )
658         tag[0] = FT_CURVE_TAG_CONIC;
659       else if ( point->flags & AF_FLAG_CUBIC )
660         tag[0] = FT_CURVE_TAG_CUBIC;
661       else
662         tag[0] = FT_CURVE_TAG_ON;
663     }
664   }
665
666
667   /****************************************************************
668    *
669    *                     EDGE POINT GRID-FITTING
670    *
671    ****************************************************************/
672
673
674   FT_LOCAL_DEF( void )
675   af_glyph_hints_align_edge_points( AF_GlyphHints  hints,
676                                     AF_Dimension   dim )
677   {
678     AF_AxisHints  axis          = & hints->axis[dim];
679     AF_Segment    segments      = axis->segments;
680     AF_Segment    segment_limit = segments + axis->num_segments;
681     AF_Segment    seg;
682
683
684     if ( dim == AF_DIMENSION_HORZ )
685     {
686       for ( seg = segments; seg < segment_limit; seg++ )
687       {
688         AF_Edge   edge = seg->edge;
689         AF_Point  point, first, last;
690
691
692         if ( edge == NULL )
693           continue;
694
695         first = seg->first;
696         last  = seg->last;
697         point = first;
698         for (;;)
699         {
700           point->x      = edge->pos;
701           point->flags |= AF_FLAG_TOUCH_X;
702
703           if ( point == last )
704             break;
705
706           point = point->next;
707
708         }
709       }
710     }
711     else
712     {
713       for ( seg = segments; seg < segment_limit; seg++ )
714       {
715         AF_Edge   edge = seg->edge;
716         AF_Point  point, first, last;
717
718
719         if ( edge == NULL )
720           continue;
721
722         first = seg->first;
723         last  = seg->last;
724         point = first;
725         for (;;)
726         {
727           point->y      = edge->pos;
728           point->flags |= AF_FLAG_TOUCH_Y;
729
730           if ( point == last )
731             break;
732
733           point = point->next;
734         }
735       }
736     }
737   }
738
739
740   /****************************************************************
741    *
742    *                    STRONG POINT INTERPOLATION
743    *
744    ****************************************************************/
745
746
747   /* hint the strong points -- this is equivalent to the TrueType `IP' */
748   /* hinting instruction                                               */
749
750   FT_LOCAL_DEF( void )
751   af_glyph_hints_align_strong_points( AF_GlyphHints  hints,
752                                       AF_Dimension   dim )
753   {
754     AF_Point      points      = hints->points;
755     AF_Point      point_limit = points + hints->num_points;
756     AF_AxisHints  axis        = &hints->axis[dim];
757     AF_Edge       edges       = axis->edges;
758     AF_Edge       edge_limit  = edges + axis->num_edges;
759     AF_Flags      touch_flag;
760
761
762     if ( dim == AF_DIMENSION_HORZ )
763       touch_flag = AF_FLAG_TOUCH_X;
764     else
765       touch_flag  = AF_FLAG_TOUCH_Y;
766
767     if ( edges < edge_limit )
768     {
769       AF_Point  point;
770       AF_Edge   edge;
771
772
773       for ( point = points; point < point_limit; point++ )
774       {
775         FT_Pos  u, ou, fu;  /* point position */
776         FT_Pos  delta;
777
778
779         if ( point->flags & touch_flag )
780           continue;
781
782         /* if this point is candidate to weak interpolation, we       */
783         /* interpolate it after all strong points have been processed */
784
785         if (  ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) &&
786              !( point->flags & AF_FLAG_INFLECTION )         )
787           continue;
788
789         if ( dim == AF_DIMENSION_VERT )
790         {
791           u  = point->fy;
792           ou = point->oy;
793         }
794         else
795         {
796           u  = point->fx;
797           ou = point->ox;
798         }
799
800         fu = u;
801
802         /* is the point before the first edge? */
803         edge  = edges;
804         delta = edge->fpos - u;
805         if ( delta >= 0 )
806         {
807           u = edge->pos - ( edge->opos - ou );
808           goto Store_Point;
809         }
810
811         /* is the point after the last edge? */
812         edge  = edge_limit - 1;
813         delta = u - edge->fpos;
814         if ( delta >= 0 )
815         {
816           u = edge->pos + ( ou - edge->opos );
817           goto Store_Point;
818         }
819
820         {
821           FT_PtrDist  min, max, mid;
822           FT_Pos      fpos;
823
824
825           /* find enclosing edges */
826           min = 0;
827           max = edge_limit - edges;
828
829 #if 1
830           /* for small edge counts, a linear search is better */
831           if ( max <= 8 )
832           {
833             FT_PtrDist  nn;
834
835             for ( nn = 0; nn < max; nn++ )
836               if ( edges[nn].fpos >= u )
837                 break;
838
839             if ( edges[nn].fpos == u )
840             {
841               u = edges[nn].pos;
842               goto Store_Point;
843             }
844             min = nn;
845           }
846           else
847 #endif
848           while ( min < max )
849           {
850             mid  = ( max + min ) >> 1;
851             edge = edges + mid;
852             fpos = edge->fpos;
853
854             if ( u < fpos )
855               max = mid;
856             else if ( u > fpos )
857               min = mid + 1;
858             else
859             {
860               /* we are on the edge */
861               u = edge->pos;
862               goto Store_Point;
863             }
864           }
865
866           {
867             AF_Edge  before = edges + min - 1;
868             AF_Edge  after  = edges + min + 0;
869
870
871             /* assert( before && after && before != after ) */
872             if ( before->scale == 0 )
873               before->scale = FT_DivFix( after->pos - before->pos,
874                                          after->fpos - before->fpos );
875
876             u = before->pos + FT_MulFix( fu - before->fpos,
877                                          before->scale );
878           }
879         }
880
881       Store_Point:
882         /* save the point position */
883         if ( dim == AF_DIMENSION_HORZ )
884           point->x = u;
885         else
886           point->y = u;
887
888         point->flags |= touch_flag;
889       }
890     }
891   }
892
893
894   /****************************************************************
895    *
896    *                    WEAK POINT INTERPOLATION
897    *
898    ****************************************************************/
899
900
901   static void
902   af_iup_shift( AF_Point  p1,
903                 AF_Point  p2,
904                 AF_Point  ref )
905   {
906     AF_Point  p;
907     FT_Pos    delta = ref->u - ref->v;
908
909     if ( delta == 0 )
910       return;
911
912     for ( p = p1; p < ref; p++ )
913       p->u = p->v + delta;
914
915     for ( p = ref + 1; p <= p2; p++ )
916       p->u = p->v + delta;
917   }
918
919
920   static void
921   af_iup_interp( AF_Point  p1,
922                  AF_Point  p2,
923                  AF_Point  ref1,
924                  AF_Point  ref2 )
925   {
926     AF_Point  p;
927     FT_Pos    u;
928     FT_Pos    v1 = ref1->v;
929     FT_Pos    v2 = ref2->v;
930     FT_Pos    d1 = ref1->u - v1;
931     FT_Pos    d2 = ref2->u - v2;
932
933
934     if ( p1 > p2 )
935       return;
936
937     if ( v1 == v2 )
938     {
939       for ( p = p1; p <= p2; p++ )
940       {
941         u = p->v;
942
943         if ( u <= v1 )
944           u += d1;
945         else
946           u += d2;
947
948         p->u = u;
949       }
950       return;
951     }
952
953     if ( v1 < v2 )
954     {
955       for ( p = p1; p <= p2; p++ )
956       {
957         u = p->v;
958
959         if ( u <= v1 )
960           u += d1;
961         else if ( u >= v2 )
962           u += d2;
963         else
964           u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
965
966         p->u = u;
967       }
968     }
969     else
970     {
971       for ( p = p1; p <= p2; p++ )
972       {
973         u = p->v;
974
975         if ( u <= v2 )
976           u += d2;
977         else if ( u >= v1 )
978           u += d1;
979         else
980           u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
981
982         p->u = u;
983       }
984     }
985   }
986
987
988   FT_LOCAL_DEF( void )
989   af_glyph_hints_align_weak_points( AF_GlyphHints  hints,
990                                     AF_Dimension   dim )
991   {
992     AF_Point   points        = hints->points;
993     AF_Point   point_limit   = points + hints->num_points;
994     AF_Point*  contour       = hints->contours;
995     AF_Point*  contour_limit = contour + hints->num_contours;
996     AF_Flags   touch_flag;
997     AF_Point   point;
998     AF_Point   end_point;
999     AF_Point   first_point;
1000
1001
1002     /* PASS 1: Move segment points to edge positions */
1003
1004     if ( dim == AF_DIMENSION_HORZ )
1005     {
1006       touch_flag = AF_FLAG_TOUCH_X;
1007
1008       for ( point = points; point < point_limit; point++ )
1009       {
1010         point->u = point->x;
1011         point->v = point->ox;
1012       }
1013     }
1014     else
1015     {
1016       touch_flag = AF_FLAG_TOUCH_Y;
1017
1018       for ( point = points; point < point_limit; point++ )
1019       {
1020         point->u = point->y;
1021         point->v = point->oy;
1022       }
1023     }
1024
1025     point = points;
1026
1027     for ( ; contour < contour_limit; contour++ )
1028     {
1029       AF_Point  first_touched, last_touched;
1030
1031
1032       point       = *contour;
1033       end_point   = point->prev;
1034       first_point = point;
1035
1036       /* find first touched point */
1037       for (;;)
1038       {
1039         if ( point > end_point )  /* no touched point in contour */
1040           goto NextContour;
1041
1042         if ( point->flags & touch_flag )
1043           break;
1044
1045         point++;
1046       }
1047
1048       first_touched = point;
1049       last_touched  = point;
1050
1051       for (;;)
1052       {
1053         FT_ASSERT( point <= end_point &&
1054                    ( point->flags & touch_flag ) != 0 );
1055
1056         /* skip any touched neighbhours */
1057         while ( point < end_point && ( point[1].flags & touch_flag ) != 0 )
1058           point++;
1059
1060         last_touched = point;
1061
1062         /* find the next touched point, if any */
1063         point ++;
1064         for (;;)
1065         {
1066           if ( point > end_point )
1067             goto EndContour;
1068
1069           if ( ( point->flags & touch_flag ) != 0 )
1070             break;
1071
1072           point++;
1073         }
1074
1075         /* interpolate between last_touched and point */
1076         af_iup_interp( last_touched + 1, point - 1,
1077                        last_touched, point );
1078       }
1079
1080     EndContour:
1081       /* special case: only one point was touched */
1082       if ( last_touched == first_touched )
1083       {
1084         af_iup_shift( first_point, end_point, first_touched );
1085       }
1086       else /* interpolate the last part */
1087       {
1088         if ( last_touched < end_point )
1089           af_iup_interp( last_touched + 1, end_point,
1090                          last_touched, first_touched );
1091
1092         if ( first_touched > points )
1093           af_iup_interp( first_point, first_touched - 1,
1094                          last_touched, first_touched );
1095       }
1096
1097     NextContour:
1098       ;
1099     }
1100
1101     /* now save the interpolated values back to x/y */
1102     if ( dim == AF_DIMENSION_HORZ )
1103     {
1104       for ( point = points; point < point_limit; point++ )
1105         point->x = point->u;
1106     }
1107     else
1108     {
1109       for ( point = points; point < point_limit; point++ )
1110         point->y = point->u;
1111     }
1112   }
1113
1114
1115 #ifdef AF_USE_WARPER
1116
1117   FT_LOCAL_DEF( void )
1118   af_glyph_hints_scale_dim( AF_GlyphHints  hints,
1119                             AF_Dimension   dim,
1120                             FT_Fixed       scale,
1121                             FT_Pos         delta )
1122   {
1123     AF_Point  points       = hints->points;
1124     AF_Point  points_limit = points + hints->num_points;
1125     AF_Point  point;
1126
1127
1128     if ( dim == AF_DIMENSION_HORZ )
1129     {
1130       for ( point = points; point < points_limit; point++ )
1131         point->x = FT_MulFix( point->fx, scale ) + delta;
1132     }
1133     else
1134     {
1135       for ( point = points; point < points_limit; point++ )
1136         point->y = FT_MulFix( point->fy, scale ) + delta;
1137     }
1138   }
1139
1140 #endif /* AF_USE_WARPER */
1141
1142 /* END */