~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/3rdparty/freetype/src/autohint/ahglyph.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************/
 
2
/*                                                                         */
 
3
/*  ahglyph.c                                                              */
 
4
/*                                                                         */
 
5
/*    Routines used to load and analyze a given glyph before hinting       */
 
6
/*    (body).                                                              */
 
7
/*                                                                         */
 
8
/*  Copyright 2000-2001, 2002, 2003, 2004 Catharon Productions Inc.        */
 
9
/*  Author: David Turner                                                   */
 
10
/*                                                                         */
 
11
/*  This file is part of the Catharon Typography Project and shall only    */
 
12
/*  be used, modified, and distributed under the terms of the Catharon     */
 
13
/*  Open Source License that should come with this file under the name     */
 
14
/*  `CatharonLicense.txt'.  By continuing to use, modify, or distribute    */
 
15
/*  this file you indicate that you have read the license and              */
 
16
/*  understand and accept it fully.                                        */
 
17
/*                                                                         */
 
18
/*  Note that this license is compatible with the FreeType license.        */
 
19
/*                                                                         */
 
20
/***************************************************************************/
 
21
 
 
22
 
 
23
#include <ft2build.h>
 
24
#include "ahglyph.h"
 
25
#include "ahangles.h"
 
26
#include "ahglobal.h"
 
27
#include "aherrors.h"
 
28
 
 
29
 
 
30
#ifdef AH_DEBUG
 
31
 
 
32
#include <stdio.h>
 
33
 
 
34
  void
 
35
  ah_dump_edges( AH_Outline  outline )
 
36
  {
 
37
    AH_Edge     edges;
 
38
    AH_Edge     edge_limit;
 
39
    AH_Segment  segments;
 
40
    FT_Int      dimension;
 
41
 
 
42
 
 
43
    edges      = outline->horz_edges;
 
44
    edge_limit = edges + outline->num_hedges;
 
45
    segments   = outline->horz_segments;
 
46
 
 
47
    for ( dimension = 1; dimension >= 0; dimension-- )
 
48
    {
 
49
      AH_Edge   edge;
 
50
 
 
51
 
 
52
      printf ( "Table of %s edges:\n",
 
53
               !dimension ? "vertical" : "horizontal" );
 
54
      printf ( "  [ index |  pos |  dir  | link |"
 
55
               " serif | blue | opos  |  pos  ]\n" );
 
56
 
 
57
      for ( edge = edges; edge < edge_limit; edge++ )
 
58
      {
 
59
        printf ( "  [ %5d | %4d | %5s | %4d | %5d |  %c  | %5.2f | %5.2f ]\n",
 
60
                 edge - edges,
 
61
                 (int)edge->fpos,
 
62
                 edge->dir == AH_DIR_UP
 
63
                   ? "up"
 
64
                   : ( edge->dir == AH_DIR_DOWN
 
65
                         ? "down"
 
66
                         : ( edge->dir == AH_DIR_LEFT
 
67
                               ? "left"
 
68
                               : ( edge->dir == AH_DIR_RIGHT
 
69
                                     ? "right"
 
70
                                     : "none" ) ) ),
 
71
                 edge->link ? ( edge->link - edges ) : -1,
 
72
                 edge->serif ? ( edge->serif - edges ) : -1,
 
73
                 edge->blue_edge ? 'y' : 'n',
 
74
                 edge->opos / 64.0,
 
75
                 edge->pos / 64.0 );
 
76
      }
 
77
 
 
78
      edges      = outline->vert_edges;
 
79
      edge_limit = edges + outline->num_vedges;
 
80
      segments   = outline->vert_segments;
 
81
    }
 
82
  }
 
83
 
 
84
 
 
85
  /* A function used to dump the array of linked segments */
 
86
  void
 
87
  ah_dump_segments( AH_Outline  outline )
 
88
  {
 
89
    AH_Segment  segments;
 
90
    AH_Segment  segment_limit;
 
91
    AH_Point    points;
 
92
    FT_Int      dimension;
 
93
 
 
94
 
 
95
    points        = outline->points;
 
96
    segments      = outline->horz_segments;
 
97
    segment_limit = segments + outline->num_hsegments;
 
98
 
 
99
    for ( dimension = 1; dimension >= 0; dimension-- )
 
100
    {
 
101
      AH_Segment  seg;
 
102
 
 
103
 
 
104
      printf ( "Table of %s segments:\n",
 
105
               !dimension ? "vertical" : "horizontal" );
 
106
      printf ( "  [ index |  pos |  dir  | link | serif |"
 
107
               " numl | first | start ]\n" );
 
108
 
 
109
      for ( seg = segments; seg < segment_limit; seg++ )
 
110
      {
 
111
        printf ( "  [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
 
112
                 seg - segments,
 
113
                 (int)seg->pos,
 
114
                 seg->dir == AH_DIR_UP
 
115
                   ? "up"
 
116
                   : ( seg->dir == AH_DIR_DOWN
 
117
                         ? "down"
 
118
                         : ( seg->dir == AH_DIR_LEFT
 
119
                               ? "left"
 
120
                               : ( seg->dir == AH_DIR_RIGHT
 
121
                                     ? "right"
 
122
                                     : "none" ) ) ),
 
123
                 seg->link ? ( seg->link - segments ) : -1,
 
124
                 seg->serif ? ( seg->serif - segments ) : -1,
 
125
                 (int)seg->num_linked,
 
126
                 seg->first - points,
 
127
                 seg->last - points );
 
128
      }
 
129
 
 
130
      segments      = outline->vert_segments;
 
131
      segment_limit = segments + outline->num_vsegments;
 
132
    }
 
133
  }
 
134
 
 
135
#endif /* AH_DEBUG */
 
136
 
 
137
 
 
138
  /* compute the direction value of a given vector */
 
139
  static AH_Direction
 
140
  ah_compute_direction( FT_Pos  dx,
 
141
                        FT_Pos  dy )
 
142
  {
 
143
    AH_Direction  dir;
 
144
    FT_Pos        ax = FT_ABS( dx );
 
145
    FT_Pos        ay = FT_ABS( dy );
 
146
 
 
147
 
 
148
    dir = AH_DIR_NONE;
 
149
 
 
150
    /* atan(1/12) == 4.7 degrees */
 
151
 
 
152
    /* test for vertical direction */
 
153
    if ( ax * 12 < ay )
 
154
    {
 
155
      dir = dy > 0 ? AH_DIR_UP : AH_DIR_DOWN;
 
156
    }
 
157
    /* test for horizontal direction */
 
158
    else if ( ay * 12 < ax )
 
159
    {
 
160
      dir = dx > 0 ? AH_DIR_RIGHT : AH_DIR_LEFT;
 
161
    }
 
162
 
 
163
    return dir;
 
164
  }
 
165
 
 
166
 
 
167
  /* this function is used by ah_get_orientation (see below) to test */
 
168
  /* the fill direction of given bbox extremum                       */
 
169
  static FT_Int
 
170
  ah_test_extremum( FT_Outline*  outline,
 
171
                    FT_Int       n )
 
172
  {
 
173
    FT_Vector  *prev, *cur, *next;
 
174
    FT_Pos      product;
 
175
    FT_Int      first, last, c;
 
176
    FT_Int      retval;
 
177
 
 
178
 
 
179
    /* we need to compute the `previous' and `next' point */
 
180
    /* for this extremum; we check whether the extremum   */
 
181
    /* is start or end of a contour and providing         */
 
182
    /* appropriate values if so                           */
 
183
    cur  = outline->points + n;
 
184
    prev = cur - 1;
 
185
    next = cur + 1;
 
186
 
 
187
    first = 0;
 
188
    for ( c = 0; c < outline->n_contours; c++ )
 
189
    {
 
190
      last = outline->contours[c];
 
191
 
 
192
      if ( n == first )
 
193
        prev = outline->points + last;
 
194
 
 
195
      if ( n == last )
 
196
        next = outline->points + first;
 
197
 
 
198
      first = last + 1;
 
199
    }
 
200
 
 
201
    /* compute the vectorial product -- since we know that the angle */
 
202
    /* is <= 180 degrees (otherwise it wouldn't be an extremum) we   */
 
203
    /* can determine the filling orientation if the product is       */
 
204
    /* either positive or negative                                   */
 
205
    product = FT_MulDiv( cur->x  - prev->x,  /* in.x  */
 
206
                         next->y - cur->y,   /* out.y */
 
207
                         0x40 )
 
208
              -
 
209
              FT_MulDiv( cur->y  - prev->y,  /* in.y  */
 
210
                         next->x - cur->x,   /* out.x */
 
211
                         0x40 );
 
212
 
 
213
    retval = 0;
 
214
    if ( product )
 
215
      retval = product > 0 ? 2 : 1;
 
216
 
 
217
    return retval;
 
218
  }
 
219
 
 
220
 
 
221
  /* Compute the orientation of path filling.  It differs between TrueType */
 
222
  /* and Type1 formats.  We could use the `FT_OUTLINE_REVERSE_FILL' flag,  */
 
223
  /* but it is better to re-compute it directly (it seems that this flag   */
 
224
  /* isn't correctly set for some weird composite glyphs currently).       */
 
225
  /*                                                                       */
 
226
  /* We do this by computing bounding box points, and computing their      */
 
227
  /* curvature.                                                            */
 
228
  /*                                                                       */
 
229
  /* The function returns either 1 or 2.                                   */
 
230
  /*                                                                       */
 
231
  static FT_Int
 
232
  ah_get_orientation( FT_Outline*  outline )
 
233
  {
 
234
    FT_BBox  box;
 
235
    FT_Int   indices_xMin, indices_yMin, indices_xMax, indices_yMax;
 
236
    FT_Int   n, last;
 
237
 
 
238
 
 
239
    indices_xMin = -1;
 
240
    indices_yMin = -1;
 
241
    indices_xMax = -1;
 
242
    indices_yMax = -1;
 
243
 
 
244
    box.xMin = box.yMin =  32767L;
 
245
    box.xMax = box.yMax = -32768L;
 
246
 
 
247
    /* is it empty? */
 
248
    if ( outline->n_contours < 1 )
 
249
      return 1;
 
250
 
 
251
    last = outline->contours[outline->n_contours - 1];
 
252
 
 
253
    for ( n = 0; n <= last; n++ )
 
254
    {
 
255
      FT_Pos  x, y;
 
256
 
 
257
 
 
258
      x = outline->points[n].x;
 
259
      if ( x < box.xMin )
 
260
      {
 
261
        box.xMin     = x;
 
262
        indices_xMin = n;
 
263
      }
 
264
      if ( x > box.xMax )
 
265
      {
 
266
        box.xMax     = x;
 
267
        indices_xMax = n;
 
268
      }
 
269
 
 
270
      y = outline->points[n].y;
 
271
      if ( y < box.yMin )
 
272
      {
 
273
        box.yMin     = y;
 
274
        indices_yMin = n;
 
275
      }
 
276
      if ( y > box.yMax )
 
277
      {
 
278
        box.yMax     = y;
 
279
        indices_yMax = n;
 
280
      }
 
281
    }
 
282
 
 
283
    /* test orientation of the extrema */
 
284
    n = ah_test_extremum( outline, indices_xMin );
 
285
    if ( n )
 
286
      goto Exit;
 
287
 
 
288
    n = ah_test_extremum( outline, indices_yMin );
 
289
    if ( n )
 
290
      goto Exit;
 
291
 
 
292
    n = ah_test_extremum( outline, indices_xMax );
 
293
    if ( n )
 
294
      goto Exit;
 
295
 
 
296
    n = ah_test_extremum( outline, indices_yMax );
 
297
    if ( !n )
 
298
      n = 1;
 
299
 
 
300
  Exit:
 
301
    return n;
 
302
  }
 
303
 
 
304
 
 
305
  /*************************************************************************/
 
306
  /*                                                                       */
 
307
  /* <Function>                                                            */
 
308
  /*    ah_outline_new                                                     */
 
309
  /*                                                                       */
 
310
  /* <Description>                                                         */
 
311
  /*    Creates a new and empty AH_OutlineRec object.                      */
 
312
  /*                                                                       */
 
313
  FT_LOCAL_DEF( FT_Error )
 
314
  ah_outline_new( FT_Memory    memory,
 
315
                  AH_Outline*  aoutline )
 
316
  {
 
317
    FT_Error    error;
 
318
    AH_Outline  outline;
 
319
 
 
320
 
 
321
    if ( !FT_NEW( outline ) )
 
322
    {
 
323
      outline->memory = memory;
 
324
      *aoutline       = outline;
 
325
    }
 
326
 
 
327
    return error;
 
328
  }
 
329
 
 
330
 
 
331
  /*************************************************************************/
 
332
  /*                                                                       */
 
333
  /* <Function>                                                            */
 
334
  /*    ah_outline_done                                                    */
 
335
  /*                                                                       */
 
336
  /* <Description>                                                         */
 
337
  /*    Destroys a given AH_OutlineRec object.                             */
 
338
  /*                                                                       */
 
339
  FT_LOCAL_DEF( void )
 
340
  ah_outline_done( AH_Outline  outline )
 
341
  {
 
342
    FT_Memory memory = outline->memory;
 
343
 
 
344
 
 
345
    FT_FREE( outline->horz_edges );
 
346
    FT_FREE( outline->horz_segments );
 
347
    FT_FREE( outline->contours );
 
348
    FT_FREE( outline->points );
 
349
 
 
350
    FT_FREE( outline );
 
351
  }
 
352
 
 
353
 
 
354
  /*************************************************************************/
 
355
  /*                                                                       */
 
356
  /* <Function>                                                            */
 
357
  /*    ah_outline_save                                                    */
 
358
  /*                                                                       */
 
359
  /* <Description>                                                         */
 
360
  /*    Saves the contents of a given AH_OutlineRec object into a face's   */
 
361
  /*    glyph slot.                                                        */
 
362
  /*                                                                       */
 
363
  FT_LOCAL_DEF( void )
 
364
  ah_outline_save( AH_Outline  outline,
 
365
                   AH_Loader   gloader )
 
366
  {
 
367
    AH_Point    point       = outline->points;
 
368
    AH_Point    point_limit = point + outline->num_points;
 
369
    FT_Vector*  vec         = gloader->current.outline.points;
 
370
    char*       tag         = gloader->current.outline.tags;
 
371
 
 
372
 
 
373
    /* we assume that the glyph loader has already been checked for storage */
 
374
    for ( ; point < point_limit; point++, vec++, tag++ )
 
375
    {
 
376
      vec->x = point->x;
 
377
      vec->y = point->y;
 
378
 
 
379
      if ( point->flags & AH_FLAG_CONIC )
 
380
        tag[0] = FT_CURVE_TAG_CONIC;
 
381
      else if ( point->flags & AH_FLAG_CUBIC )
 
382
        tag[0] = FT_CURVE_TAG_CUBIC;
 
383
      else
 
384
        tag[0] = FT_CURVE_TAG_ON;
 
385
    }
 
386
  }
 
387
 
 
388
 
 
389
  /*************************************************************************/
 
390
  /*                                                                       */
 
391
  /* <Function>                                                            */
 
392
  /*    ah_outline_load                                                    */
 
393
  /*                                                                       */
 
394
  /* <Description>                                                         */
 
395
  /*    Loads an unscaled outline from a glyph slot into an AH_OutlineRec  */
 
396
  /*    object.                                                            */
 
397
  /*                                                                       */
 
398
  FT_LOCAL_DEF( FT_Error )
 
399
  ah_outline_load( AH_Outline  outline,
 
400
                   FT_Fixed    x_scale,
 
401
                   FT_Fixed    y_scale,
 
402
                   FT_Face     face )
 
403
  {
 
404
    FT_Memory    memory       = outline->memory;
 
405
    FT_Error     error        = AH_Err_Ok;
 
406
    FT_Outline*  source       = &face->glyph->outline;
 
407
    FT_Int       num_points   = source->n_points;
 
408
    FT_Int       num_contours = source->n_contours;
 
409
    AH_Point     points;
 
410
 
 
411
 
 
412
    /* check arguments */
 
413
    if ( !face                                          ||
 
414
         !face->size                                    ||
 
415
         face->glyph->format != FT_GLYPH_FORMAT_OUTLINE )
 
416
      return AH_Err_Invalid_Argument;
 
417
 
 
418
    /* first of all, reallocate the contours array if necessary */
 
419
    if ( num_contours > outline->max_contours )
 
420
    {
 
421
      FT_Int  new_contours = FT_PAD_CEIL( num_contours, 4 );
 
422
 
 
423
 
 
424
      if ( FT_RENEW_ARRAY( outline->contours,
 
425
                           outline->max_contours,
 
426
                           new_contours ) )
 
427
        goto Exit;
 
428
 
 
429
      outline->max_contours = new_contours;
 
430
    }
 
431
 
 
432
    /* then, reallocate the points, segments & edges arrays if needed -- */
 
433
    /* note that we reserved two additional point positions, used to     */
 
434
    /* hint metrics appropriately                                        */
 
435
    /*                                                                   */
 
436
    if ( num_points + 2 > outline->max_points )
 
437
    {
 
438
      FT_Int  news = FT_PAD_CEIL( num_points + 2, 8 );
 
439
      FT_Int  max  = outline->max_points;
 
440
 
 
441
 
 
442
      if ( FT_RENEW_ARRAY( outline->points,        max,     news     ) ||
 
443
           FT_RENEW_ARRAY( outline->horz_edges,    max * 2, news * 2 ) ||
 
444
           FT_RENEW_ARRAY( outline->horz_segments, max * 2, news * 2 ) )
 
445
        goto Exit;
 
446
 
 
447
      /* readjust some pointers */
 
448
      outline->vert_edges    = outline->horz_edges    + news;
 
449
      outline->vert_segments = outline->horz_segments + news;
 
450
      outline->max_points    = news;
 
451
    }
 
452
 
 
453
    outline->num_points   = num_points;
 
454
    outline->num_contours = num_contours;
 
455
 
 
456
    outline->num_hedges    = 0;
 
457
    outline->num_vedges    = 0;
 
458
    outline->num_hsegments = 0;
 
459
    outline->num_vsegments = 0;
 
460
 
 
461
    /* We can't rely on the value of `FT_Outline.flags' to know the fill  */
 
462
    /* direction used for a glyph, given that some fonts are broken (e.g. */
 
463
    /* the Arphic ones).  We thus recompute it each time we need to.      */
 
464
    /*                                                                    */
 
465
    outline->vert_major_dir = AH_DIR_UP;
 
466
    outline->horz_major_dir = AH_DIR_LEFT;
 
467
 
 
468
    if ( ah_get_orientation( source ) > 1 )
 
469
    {
 
470
      outline->vert_major_dir = AH_DIR_DOWN;
 
471
      outline->horz_major_dir = AH_DIR_RIGHT;
 
472
    }
 
473
 
 
474
    outline->x_scale = x_scale;
 
475
    outline->y_scale = y_scale;
 
476
 
 
477
    points = outline->points;
 
478
    if ( outline->num_points == 0 )
 
479
      goto Exit;
 
480
 
 
481
    {
 
482
      /* do one thing at a time -- it is easier to understand, and */
 
483
      /* the code is clearer                                       */
 
484
      AH_Point  point;
 
485
      AH_Point  point_limit = points + outline->num_points;
 
486
 
 
487
 
 
488
      /* compute coordinates */
 
489
      {
 
490
        FT_Vector*  vec = source->points;
 
491
 
 
492
 
 
493
        for ( point = points; point < point_limit; vec++, point++ )
 
494
        {
 
495
          point->fx = vec->x;
 
496
          point->fy = vec->y;
 
497
          point->ox = point->x = FT_MulFix( vec->x, x_scale );
 
498
          point->oy = point->y = FT_MulFix( vec->y, y_scale );
 
499
 
 
500
          point->flags = 0;
 
501
        }
 
502
      }
 
503
 
 
504
      /* compute Bezier flags */
 
505
      {
 
506
        char*  tag = source->tags;
 
507
 
 
508
 
 
509
        for ( point = points; point < point_limit; point++, tag++ )
 
510
        {
 
511
          switch ( FT_CURVE_TAG( *tag ) )
 
512
          {
 
513
          case FT_CURVE_TAG_CONIC:
 
514
            point->flags = AH_FLAG_CONIC;
 
515
            break;
 
516
          case FT_CURVE_TAG_CUBIC:
 
517
            point->flags = AH_FLAG_CUBIC;
 
518
            break;
 
519
          default:
 
520
            ;
 
521
          }
 
522
        }
 
523
      }
 
524
 
 
525
      /* compute `next' and `prev' */
 
526
      {
 
527
        FT_Int    contour_index;
 
528
        AH_Point  prev;
 
529
        AH_Point  first;
 
530
        AH_Point  end;
 
531
 
 
532
 
 
533
        contour_index = 0;
 
534
 
 
535
        first = points;
 
536
        end   = points + source->contours[0];
 
537
        prev  = end;
 
538
 
 
539
        for ( point = points; point < point_limit; point++ )
 
540
        {
 
541
          point->prev = prev;
 
542
          if ( point < end )
 
543
          {
 
544
            point->next = point + 1;
 
545
            prev        = point;
 
546
          }
 
547
          else
 
548
          {
 
549
            point->next = first;
 
550
            contour_index++;
 
551
            if ( point + 1 < point_limit )
 
552
            {
 
553
              end   = points + source->contours[contour_index];
 
554
              first = point + 1;
 
555
              prev  = end;
 
556
            }
 
557
          }
 
558
        }
 
559
      }
 
560
 
 
561
      /* set-up the contours array */
 
562
      {
 
563
        AH_Point*  contour       = outline->contours;
 
564
        AH_Point*  contour_limit = contour + outline->num_contours;
 
565
        short*     end           = source->contours;
 
566
        short      idx           = 0;
 
567
 
 
568
 
 
569
        for ( ; contour < contour_limit; contour++, end++ )
 
570
        {
 
571
          contour[0] = points + idx;
 
572
          idx        = (short)( end[0] + 1 );
 
573
        }
 
574
      }
 
575
 
 
576
      /* compute directions of in & out vectors */
 
577
      {
 
578
        for ( point = points; point < point_limit; point++ )
 
579
        {
 
580
          AH_Point   prev;
 
581
          AH_Point   next;
 
582
          FT_Vector  ivec, ovec;
 
583
 
 
584
 
 
585
          prev   = point->prev;
 
586
          ivec.x = point->fx - prev->fx;
 
587
          ivec.y = point->fy - prev->fy;
 
588
 
 
589
          point->in_dir = ah_compute_direction( ivec.x, ivec.y );
 
590
 
 
591
          next   = point->next;
 
592
          ovec.x = next->fx - point->fx;
 
593
          ovec.y = next->fy - point->fy;
 
594
 
 
595
          point->out_dir = ah_compute_direction( ovec.x, ovec.y );
 
596
 
 
597
#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
 
598
          if ( point->flags & ( AH_FLAG_CONIC | AH_FLAG_CUBIC ) )
 
599
          {
 
600
          Is_Weak_Point:
 
601
            point->flags |= AH_FLAG_WEAK_INTERPOLATION;
 
602
          }
 
603
          else if ( point->out_dir == point->in_dir )
 
604
          {
 
605
            AH_Angle  angle_in, angle_out, delta;
 
606
 
 
607
 
 
608
            if ( point->out_dir != AH_DIR_NONE )
 
609
              goto Is_Weak_Point;
 
610
 
 
611
            angle_in  = ah_angle( &ivec );
 
612
            angle_out = ah_angle( &ovec );
 
613
            delta     = angle_in - angle_out;
 
614
 
 
615
            if ( delta > AH_PI )
 
616
              delta = AH_2PI - delta;
 
617
 
 
618
            if ( delta < 0 )
 
619
              delta = -delta;
 
620
 
 
621
            if ( delta < 2 )
 
622
              goto Is_Weak_Point;
 
623
          }
 
624
          else if ( point->in_dir == -point->out_dir )
 
625
            goto Is_Weak_Point;
 
626
#endif
 
627
        }
 
628
      }
 
629
    }
 
630
 
 
631
  Exit:
 
632
    return error;
 
633
  }
 
634
 
 
635
 
 
636
  FT_LOCAL_DEF( void )
 
637
  ah_setup_uv( AH_Outline  outline,
 
638
               AH_UV       source )
 
639
  {
 
640
    AH_Point  point       = outline->points;
 
641
    AH_Point  point_limit = point + outline->num_points;
 
642
 
 
643
 
 
644
    switch ( source )
 
645
    {
 
646
    case AH_UV_FXY:
 
647
      for ( ; point < point_limit; point++ )
 
648
      {
 
649
        point->u = point->fx;
 
650
        point->v = point->fy;
 
651
      }
 
652
      break;
 
653
 
 
654
    case AH_UV_FYX:
 
655
      for ( ; point < point_limit; point++ )
 
656
      {
 
657
        point->u = point->fy;
 
658
        point->v = point->fx;
 
659
      }
 
660
      break;
 
661
 
 
662
    case AH_UV_OXY:
 
663
      for ( ; point < point_limit; point++ )
 
664
      {
 
665
        point->u = point->ox;
 
666
        point->v = point->oy;
 
667
      }
 
668
      break;
 
669
 
 
670
    case AH_UV_OYX:
 
671
      for ( ; point < point_limit; point++ )
 
672
      {
 
673
        point->u = point->oy;
 
674
        point->v = point->ox;
 
675
      }
 
676
      break;
 
677
 
 
678
    case AH_UV_YX:
 
679
      for ( ; point < point_limit; point++ )
 
680
      {
 
681
        point->u = point->y;
 
682
        point->v = point->x;
 
683
      }
 
684
      break;
 
685
 
 
686
    case AH_UV_OX:
 
687
      for ( ; point < point_limit; point++ )
 
688
      {
 
689
        point->u = point->x;
 
690
        point->v = point->ox;
 
691
      }
 
692
      break;
 
693
 
 
694
    case AH_UV_OY:
 
695
      for ( ; point < point_limit; point++ )
 
696
      {
 
697
        point->u = point->y;
 
698
        point->v = point->oy;
 
699
      }
 
700
      break;
 
701
 
 
702
    default:
 
703
      for ( ; point < point_limit; point++ )
 
704
      {
 
705
        point->u = point->x;
 
706
        point->v = point->y;
 
707
      }
 
708
    }
 
709
  }
 
710
 
 
711
 
 
712
  /* compute all inflex points in a given glyph */
 
713
  static void
 
714
  ah_outline_compute_inflections( AH_Outline  outline )
 
715
  {
 
716
    AH_Point*  contour       = outline->contours;
 
717
    AH_Point*  contour_limit = contour + outline->num_contours;
 
718
 
 
719
 
 
720
    /* load original coordinates in (u,v) */
 
721
    ah_setup_uv( outline, AH_UV_FXY );
 
722
 
 
723
    /* do each contour separately */
 
724
    for ( ; contour < contour_limit; contour++ )
 
725
    {
 
726
      FT_Vector  vec;
 
727
      AH_Point   point = contour[0];
 
728
      AH_Point   first = point;
 
729
      AH_Point   start = point;
 
730
      AH_Point   end   = point;
 
731
      AH_Point   before;
 
732
      AH_Point   after;
 
733
      AH_Angle   angle_in, angle_seg, angle_out;
 
734
      AH_Angle   diff_in, diff_out;
 
735
      FT_Int     finished = 0;
 
736
 
 
737
 
 
738
      /* compute first segment in contour */
 
739
      first = point;
 
740
 
 
741
      start = end = first;
 
742
      do
 
743
      {
 
744
        end = end->next;
 
745
        if ( end == first )
 
746
          goto Skip;
 
747
 
 
748
      } while ( end->u == first->u && end->v == first->v );
 
749
 
 
750
      vec.x = end->u - start->u;
 
751
      vec.y = end->v - start->v;
 
752
      angle_seg = ah_angle( &vec );
 
753
 
 
754
      /* extend the segment start whenever possible */
 
755
      before = start;
 
756
      do
 
757
      {
 
758
        do
 
759
        {
 
760
          start  = before;
 
761
          before = before->prev;
 
762
          if ( before == first )
 
763
            goto Skip;
 
764
 
 
765
        } while ( before->u == start->u && before->v == start->v );
 
766
 
 
767
        vec.x    = start->u - before->u;
 
768
        vec.y    = start->v - before->v;
 
769
        angle_in = ah_angle( &vec );
 
770
 
 
771
      } while ( angle_in == angle_seg );
 
772
 
 
773
      first   = start;
 
774
      diff_in = ah_angle_diff( angle_in, angle_seg );
 
775
 
 
776
      /* now, process all segments in the contour */
 
777
      do
 
778
      {
 
779
        /* first, extend current segment's end whenever possible */
 
780
        after = end;
 
781
        do
 
782
        {
 
783
          do
 
784
          {
 
785
            end   = after;
 
786
            after = after->next;
 
787
            if ( after == first )
 
788
              finished = 1;
 
789
 
 
790
          } while ( end->u == after->u && end->v == after->v );
 
791
 
 
792
          vec.x     = after->u - end->u;
 
793
          vec.y     = after->v - end->v;
 
794
          angle_out = ah_angle( &vec );
 
795
 
 
796
        } while ( angle_out == angle_seg );
 
797
 
 
798
        diff_out = ah_angle_diff( angle_seg, angle_out );
 
799
 
 
800
        if ( ( diff_in ^ diff_out ) < 0 )
 
801
        {
 
802
          /* diff_in and diff_out have different signs, we have */
 
803
          /* inflection points here...                          */
 
804
          do
 
805
          {
 
806
            start->flags |= AH_FLAG_INFLECTION;
 
807
            start = start->next;
 
808
 
 
809
          } while ( start != end );
 
810
 
 
811
          start->flags |= AH_FLAG_INFLECTION;
 
812
        }
 
813
 
 
814
        start     = end;
 
815
        end       = after;
 
816
        angle_seg = angle_out;
 
817
        diff_in   = diff_out;
 
818
 
 
819
      } while ( !finished );
 
820
 
 
821
    Skip:
 
822
      ;
 
823
    }
 
824
  }
 
825
 
 
826
 
 
827
  FT_LOCAL_DEF( void )
 
828
  ah_outline_compute_segments( AH_Outline  outline )
 
829
  {
 
830
    int           dimension;
 
831
    AH_Segment    segments;
 
832
    FT_Int*       p_num_segments;
 
833
    AH_Direction  segment_dir;
 
834
    AH_Direction  major_dir;
 
835
 
 
836
 
 
837
    segments       = outline->horz_segments;
 
838
    p_num_segments = &outline->num_hsegments;
 
839
    major_dir      = AH_DIR_RIGHT;      /* This value must be positive! */
 
840
    segment_dir    = major_dir;
 
841
 
 
842
    /* set up (u,v) in each point */
 
843
    ah_setup_uv( outline, AH_UV_FYX );
 
844
 
 
845
    for ( dimension = 1; dimension >= 0; dimension-- )
 
846
    {
 
847
      AH_Point*   contour       =  outline->contours;
 
848
      AH_Point*   contour_limit =  contour + outline->num_contours;
 
849
      AH_Segment  segment       =  segments;
 
850
      FT_Int      num_segments  =  0;
 
851
 
 
852
#ifdef AH_HINT_METRICS
 
853
      AH_Point    min_point     =  0;
 
854
      AH_Point    max_point     =  0;
 
855
      FT_Pos      min_coord     =  32000;
 
856
      FT_Pos      max_coord     = -32000;
 
857
#endif
 
858
 
 
859
 
 
860
      /* do each contour separately */
 
861
      for ( ; contour < contour_limit; contour++ )
 
862
      {
 
863
        AH_Point  point   =  contour[0];
 
864
        AH_Point  last    =  point->prev;
 
865
        int       on_edge =  0;
 
866
        FT_Pos    min_pos =  32000;  /* minimum segment pos != min_coord */
 
867
        FT_Pos    max_pos = -32000;  /* maximum segment pos != max_coord */
 
868
        FT_Bool   passed;
 
869
 
 
870
 
 
871
#ifdef AH_HINT_METRICS
 
872
        if ( point->u < min_coord )
 
873
        {
 
874
          min_coord = point->u;
 
875
          min_point = point;
 
876
        }
 
877
        if ( point->u > max_coord )
 
878
        {
 
879
          max_coord = point->u;
 
880
          max_point = point;
 
881
        }
 
882
#endif
 
883
 
 
884
        if ( point == last )  /* skip singletons -- just in case */
 
885
          continue;
 
886
 
 
887
        if ( FT_ABS( last->out_dir )  == major_dir &&
 
888
             FT_ABS( point->out_dir ) == major_dir )
 
889
        {
 
890
          /* we are already on an edge, try to locate its start */
 
891
          last = point;
 
892
 
 
893
          for (;;)
 
894
          {
 
895
            point = point->prev;
 
896
            if ( FT_ABS( point->out_dir ) != major_dir )
 
897
            {
 
898
              point = point->next;
 
899
              break;
 
900
            }
 
901
            if ( point == last )
 
902
              break;
 
903
          }
 
904
        }
 
905
 
 
906
        last   = point;
 
907
        passed = 0;
 
908
 
 
909
        for (;;)
 
910
        {
 
911
          FT_Pos  u, v;
 
912
 
 
913
 
 
914
          if ( on_edge )
 
915
          {
 
916
            u = point->u;
 
917
            if ( u < min_pos )
 
918
              min_pos = u;
 
919
            if ( u > max_pos )
 
920
              max_pos = u;
 
921
 
 
922
            if ( point->out_dir != segment_dir || point == last )
 
923
            {
 
924
              /* we are just leaving an edge; record a new segment! */
 
925
              segment->last = point;
 
926
              segment->pos  = ( min_pos + max_pos ) >> 1;
 
927
 
 
928
              /* a segment is round if either its first or last point */
 
929
              /* is a control point                                   */
 
930
              if ( ( segment->first->flags | point->flags ) &
 
931
                     AH_FLAG_CONTROL                        )
 
932
                segment->flags |= AH_EDGE_ROUND;
 
933
 
 
934
              /* compute segment size */
 
935
              min_pos = max_pos = point->v;
 
936
 
 
937
              v = segment->first->v;
 
938
              if ( v < min_pos )
 
939
                min_pos = v;
 
940
              if ( v > max_pos )
 
941
                max_pos = v;
 
942
 
 
943
              segment->min_coord = min_pos;
 
944
              segment->max_coord = max_pos;
 
945
 
 
946
              on_edge = 0;
 
947
              num_segments++;
 
948
              segment++;
 
949
              /* fallthrough */
 
950
            }
 
951
          }
 
952
 
 
953
          /* now exit if we are at the start/end point */
 
954
          if ( point == last )
 
955
          {
 
956
            if ( passed )
 
957
              break;
 
958
            passed = 1;
 
959
          }
 
960
 
 
961
          if ( !on_edge && FT_ABS( point->out_dir ) == major_dir )
 
962
          {
 
963
            /* this is the start of a new segment! */
 
964
            segment_dir = point->out_dir;
 
965
 
 
966
            /* clear all segment fields */
 
967
            FT_ZERO( segment );
 
968
 
 
969
            segment->dir      = segment_dir;
 
970
            segment->flags    = AH_EDGE_NORMAL;
 
971
            min_pos = max_pos = point->u;
 
972
            segment->first    = point;
 
973
            segment->last     = point;
 
974
            segment->contour  = contour;
 
975
            segment->score    = 32000;
 
976
            segment->link     = NULL;
 
977
            on_edge           = 1;
 
978
 
 
979
#ifdef AH_HINT_METRICS
 
980
            if ( point == max_point )
 
981
              max_point = 0;
 
982
 
 
983
            if ( point == min_point )
 
984
              min_point = 0;
 
985
#endif
 
986
          }
 
987
 
 
988
          point = point->next;
 
989
        }
 
990
 
 
991
      } /* contours */
 
992
 
 
993
#ifdef AH_HINT_METRICS
 
994
      /* we need to ensure that there are edges on the left-most and  */
 
995
      /* right-most points of the glyph in order to hint the metrics; */
 
996
      /* we do this by inserting fake segments when needed            */
 
997
      if ( dimension == 0 )
 
998
      {
 
999
        AH_Point  point       = outline->points;
 
1000
        AH_Point  point_limit = point + outline->num_points;
 
1001
 
 
1002
        FT_Pos    min_pos =  32000;
 
1003
        FT_Pos    max_pos = -32000;
 
1004
 
 
1005
 
 
1006
        min_point = 0;
 
1007
        max_point = 0;
 
1008
 
 
1009
        /* compute minimum and maximum points */
 
1010
        for ( ; point < point_limit; point++ )
 
1011
        {
 
1012
          FT_Pos  x = point->fx;
 
1013
 
 
1014
 
 
1015
          if ( x < min_pos )
 
1016
          {
 
1017
            min_pos   = x;
 
1018
            min_point = point;
 
1019
          }
 
1020
          if ( x > max_pos )
 
1021
          {
 
1022
            max_pos   = x;
 
1023
            max_point = point;
 
1024
          }
 
1025
        }
 
1026
 
 
1027
        /* insert minimum segment */
 
1028
        if ( min_point )
 
1029
        {
 
1030
          /* clear all segment fields */
 
1031
          FT_ZERO( segment );
 
1032
 
 
1033
          segment->dir   = segment_dir;
 
1034
          segment->flags = AH_EDGE_NORMAL;
 
1035
          segment->first = min_point;
 
1036
          segment->last  = min_point;
 
1037
          segment->pos   = min_pos;
 
1038
          segment->score = 32000;
 
1039
          segment->link  = NULL;
 
1040
 
 
1041
          num_segments++;
 
1042
          segment++;
 
1043
        }
 
1044
 
 
1045
        /* insert maximum segment */
 
1046
        if ( max_point )
 
1047
        {
 
1048
          /* clear all segment fields */
 
1049
          FT_ZERO( segment );
 
1050
 
 
1051
          segment->dir   = segment_dir;
 
1052
          segment->flags = AH_EDGE_NORMAL;
 
1053
          segment->first = max_point;
 
1054
          segment->last  = max_point;
 
1055
          segment->pos   = max_pos;
 
1056
          segment->score = 32000;
 
1057
          segment->link  = NULL;
 
1058
 
 
1059
          num_segments++;
 
1060
          segment++;
 
1061
        }
 
1062
      }
 
1063
#endif /* AH_HINT_METRICS */
 
1064
 
 
1065
      *p_num_segments = num_segments;
 
1066
 
 
1067
      segments       = outline->vert_segments;
 
1068
      major_dir      = AH_DIR_UP;
 
1069
      p_num_segments = &outline->num_vsegments;
 
1070
 
 
1071
      ah_setup_uv( outline, AH_UV_FXY );
 
1072
    }
 
1073
  }
 
1074
 
 
1075
 
 
1076
  FT_LOCAL_DEF( void )
 
1077
  ah_outline_link_segments( AH_Outline  outline )
 
1078
  {
 
1079
    AH_Segment    segments;
 
1080
    AH_Segment    segment_limit;
 
1081
    AH_Direction  major_dir;
 
1082
    int           dimension;
 
1083
 
 
1084
 
 
1085
    segments      = outline->horz_segments;
 
1086
    segment_limit = segments + outline->num_hsegments;
 
1087
    major_dir     = outline->horz_major_dir;
 
1088
 
 
1089
    for ( dimension = 1; dimension >= 0; dimension-- )
 
1090
    {
 
1091
      AH_Segment  seg1;
 
1092
      AH_Segment  seg2;
 
1093
 
 
1094
#if 0
 
1095
      /* now compare each segment to the others */
 
1096
      for ( seg1 = segments; seg1 < segment_limit; seg1++ )
 
1097
      {
 
1098
        FT_Pos      best_score;
 
1099
        AH_Segment  best_segment;
 
1100
 
 
1101
 
 
1102
        /* the fake segments are introduced to hint the metrics -- */
 
1103
        /* we must never link them to anything                     */
 
1104
        if ( seg1->first == seg1->last )
 
1105
          continue;
 
1106
 
 
1107
        best_segment = seg1->link;
 
1108
        if ( best_segment )
 
1109
          best_score = seg1->score;
 
1110
        else
 
1111
          best_score = +32000;
 
1112
 
 
1113
        for ( seg2 = segments; seg2 < segment_limit; seg2++ )
 
1114
          if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 )
 
1115
          {
 
1116
            FT_Pos   pos1 = seg1->pos;
 
1117
            FT_Pos   pos2 = seg2->pos;
 
1118
            FT_Bool  is_dir;
 
1119
            FT_Bool  is_pos;
 
1120
 
 
1121
 
 
1122
            /* check that the segments are correctly oriented and */
 
1123
            /* positioned to form a black distance                */
 
1124
 
 
1125
            is_dir = (FT_Bool)( seg1->dir == outline->horz_major_dir ||
 
1126
                                seg1->dir == outline->vert_major_dir );
 
1127
            is_pos = (FT_Bool)( pos1 > pos2 );
 
1128
 
 
1129
            if ( pos1 == pos2 || !(is_dir ^ is_pos) )
 
1130
              continue;
 
1131
 
 
1132
            {
 
1133
              FT_Pos  min = seg1->min_coord;
 
1134
              FT_Pos  max = seg1->max_coord;
 
1135
              FT_Pos  len, dist, score;
 
1136
 
 
1137
 
 
1138
              if ( min < seg2->min_coord )
 
1139
                min = seg2->min_coord;
 
1140
 
 
1141
              if ( max > seg2->max_coord )
 
1142
                max = seg2->max_coord;
 
1143
 
 
1144
              len = max - min;
 
1145
              if ( len >= 8 )
 
1146
              {
 
1147
                dist = seg2->pos - seg1->pos;
 
1148
                if ( dist < 0 )
 
1149
                  dist = -dist;
 
1150
 
 
1151
                score = dist + 3000 / len;
 
1152
 
 
1153
                if ( score < best_score )
 
1154
                {
 
1155
                  best_score   = score;
 
1156
                  best_segment = seg2;
 
1157
                }
 
1158
              }
 
1159
            }
 
1160
          }
 
1161
 
 
1162
        if ( best_segment )
 
1163
        {
 
1164
          seg1->link  = best_segment;
 
1165
          seg1->score = best_score;
 
1166
          best_segment->num_linked++;
 
1167
        }
 
1168
      }
 
1169
#endif /* 0 */
 
1170
 
 
1171
#if 1
 
1172
      /* the following code does the same, but much faster! */
 
1173
 
 
1174
      /* now compare each segment to the others */
 
1175
      for ( seg1 = segments; seg1 < segment_limit; seg1++ )
 
1176
      {
 
1177
        /* the fake segments are introduced to hint the metrics -- */
 
1178
        /* we must never link them to anything                     */
 
1179
        if ( seg1->first == seg1->last || seg1->dir != major_dir )
 
1180
          continue;
 
1181
 
 
1182
        for ( seg2 = segments; seg2 < segment_limit; seg2++ )
 
1183
          if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 )
 
1184
          {
 
1185
            FT_Pos  pos1 = seg1->pos;
 
1186
            FT_Pos  pos2 = seg2->pos;
 
1187
            FT_Pos  dist = pos2 - pos1;
 
1188
 
 
1189
 
 
1190
            if ( dist < 0 )
 
1191
              continue;
 
1192
 
 
1193
            {
 
1194
              FT_Pos  min = seg1->min_coord;
 
1195
              FT_Pos  max = seg1->max_coord;
 
1196
              FT_Pos  len, score;
 
1197
 
 
1198
 
 
1199
              if ( min < seg2->min_coord )
 
1200
                min = seg2->min_coord;
 
1201
 
 
1202
              if ( max > seg2->max_coord )
 
1203
                max = seg2->max_coord;
 
1204
 
 
1205
              len = max - min;
 
1206
              if ( len >= 8 )
 
1207
              {
 
1208
                score = dist + 3000 / len;
 
1209
 
 
1210
                if ( score < seg1->score )
 
1211
                {
 
1212
                  seg1->score = score;
 
1213
                  seg1->link  = seg2;
 
1214
                }
 
1215
 
 
1216
                if ( score < seg2->score )
 
1217
                {
 
1218
                  seg2->score = score;
 
1219
                  seg2->link  = seg1;
 
1220
                }
 
1221
              }
 
1222
            }
 
1223
          }
 
1224
      }
 
1225
#endif /* 1 */
 
1226
 
 
1227
      /* now, compute the `serif' segments */
 
1228
      for ( seg1 = segments; seg1 < segment_limit; seg1++ )
 
1229
      {
 
1230
        seg2 = seg1->link;
 
1231
 
 
1232
        if ( seg2 )
 
1233
        {
 
1234
          seg2->num_linked++;
 
1235
          if ( seg2->link != seg1 )
 
1236
          {
 
1237
            seg1->link  = 0;
 
1238
            seg1->serif = seg2->link;
 
1239
          }
 
1240
        }
 
1241
      }
 
1242
 
 
1243
      segments      = outline->vert_segments;
 
1244
      segment_limit = segments + outline->num_vsegments;
 
1245
      major_dir     = outline->vert_major_dir;
 
1246
    }
 
1247
  }
 
1248
 
 
1249
 
 
1250
  static void
 
1251
  ah_outline_compute_edges( AH_Outline  outline )
 
1252
  {
 
1253
    AH_Edge       edges;
 
1254
    AH_Segment    segments;
 
1255
    AH_Segment    segment_limit;
 
1256
    AH_Direction  up_dir;
 
1257
    FT_Int*       p_num_edges;
 
1258
    FT_Int        dimension;
 
1259
    FT_Fixed      scale;
 
1260
    FT_Pos        edge_distance_threshold;
 
1261
 
 
1262
 
 
1263
    edges         = outline->horz_edges;
 
1264
    segments      = outline->horz_segments;
 
1265
    segment_limit = segments + outline->num_hsegments;
 
1266
    p_num_edges   = &outline->num_hedges;
 
1267
    up_dir        = AH_DIR_RIGHT;
 
1268
    scale         = outline->y_scale;
 
1269
 
 
1270
    for ( dimension = 1; dimension >= 0; dimension-- )
 
1271
    {
 
1272
      AH_Edge     edge;
 
1273
      AH_Edge     edge_limit;  /* really == edge + num_edges */
 
1274
      AH_Segment  seg;
 
1275
 
 
1276
 
 
1277
      /*********************************************************************/
 
1278
      /*                                                                   */
 
1279
      /* We will begin by generating a sorted table of edges for the       */
 
1280
      /* current direction.  To do so, we simply scan each segment and try */
 
1281
      /* to find an edge in our table that corresponds to its position.    */
 
1282
      /*                                                                   */
 
1283
      /* If no edge is found, we create and insert a new edge in the       */
 
1284
      /* sorted table.  Otherwise, we simply add the segment to the edge's */
 
1285
      /* list which will be processed in the second step to compute the    */
 
1286
      /* edge's properties.                                                */
 
1287
      /*                                                                   */
 
1288
      /* Note that the edges table is sorted along the segment/edge        */
 
1289
      /* position.                                                         */
 
1290
      /*                                                                   */
 
1291
      /*********************************************************************/
 
1292
 
 
1293
      edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold,
 
1294
                                           scale );
 
1295
      if ( edge_distance_threshold > 64 / 4 )
 
1296
        edge_distance_threshold = 64 / 4;
 
1297
 
 
1298
      edge_distance_threshold = FT_DivFix( edge_distance_threshold,
 
1299
                                           scale );
 
1300
 
 
1301
      edge_limit = edges;
 
1302
      for ( seg = segments; seg < segment_limit; seg++ )
 
1303
      {
 
1304
        AH_Edge  found = 0;
 
1305
 
 
1306
 
 
1307
        /* look for an edge corresponding to the segment */
 
1308
        for ( edge = edges; edge < edge_limit; edge++ )
 
1309
        {
 
1310
          FT_Pos  dist;
 
1311
 
 
1312
 
 
1313
          dist = seg->pos - edge->fpos;
 
1314
          if ( dist < 0 )
 
1315
            dist = -dist;
 
1316
 
 
1317
          if ( dist < edge_distance_threshold )
 
1318
          {
 
1319
            found = edge;
 
1320
            break;
 
1321
          }
 
1322
        }
 
1323
 
 
1324
        if ( !found )
 
1325
        {
 
1326
          /* insert a new edge in the list and */
 
1327
          /* sort according to the position    */
 
1328
          while ( edge > edges && edge[-1].fpos > seg->pos )
 
1329
          {
 
1330
            edge[0] = edge[-1];
 
1331
            edge--;
 
1332
          }
 
1333
          edge_limit++;
 
1334
 
 
1335
          /* clear all edge fields */
 
1336
          FT_MEM_ZERO( edge, sizeof ( *edge ) );
 
1337
 
 
1338
          /* add the segment to the new edge's list */
 
1339
          edge->first    = seg;
 
1340
          edge->last     = seg;
 
1341
          edge->fpos     = seg->pos;
 
1342
          edge->opos     = edge->pos = FT_MulFix( seg->pos, scale );
 
1343
          seg->edge_next = seg;
 
1344
        }
 
1345
        else
 
1346
        {
 
1347
          /* if an edge was found, simply add the segment to the edge's */
 
1348
          /* list                                                       */
 
1349
          seg->edge_next        = edge->first;
 
1350
          edge->last->edge_next = seg;
 
1351
          edge->last            = seg;
 
1352
        }
 
1353
      }
 
1354
      *p_num_edges = (FT_Int)( edge_limit - edges );
 
1355
 
 
1356
 
 
1357
      /*********************************************************************/
 
1358
      /*                                                                   */
 
1359
      /* Good, we will now compute each edge's properties according to     */
 
1360
      /* segments found on its position.  Basically, these are:            */
 
1361
      /*                                                                   */
 
1362
      /*  - edge's main direction                                          */
 
1363
      /*  - stem edge, serif edge or both (which defaults to stem then)    */
 
1364
      /*  - rounded edge, straight or both (which defaults to straight)    */
 
1365
      /*  - link for edge                                                  */
 
1366
      /*                                                                   */
 
1367
      /*********************************************************************/
 
1368
 
 
1369
      /* first of all, set the `edge' field in each segment -- this is */
 
1370
      /* required in order to compute edge links                       */
 
1371
 
 
1372
      /* Note that I've tried to remove this loop, setting
 
1373
       * the "edge" field of each segment directly in the
 
1374
       * code above.  For some reason, it slows down execution
 
1375
       * speed -- on a Sun.
 
1376
       */
 
1377
      for ( edge = edges; edge < edge_limit; edge++ )
 
1378
      {
 
1379
        seg = edge->first;
 
1380
        if ( seg )
 
1381
          do
 
1382
          {
 
1383
            seg->edge = edge;
 
1384
            seg       = seg->edge_next;
 
1385
          }
 
1386
          while ( seg != edge->first );
 
1387
      }
 
1388
 
 
1389
      /* now, compute each edge properties */
 
1390
      for ( edge = edges; edge < edge_limit; edge++ )
 
1391
      {
 
1392
        FT_Int  is_round    = 0;  /* does it contain round segments?    */
 
1393
        FT_Int  is_straight = 0;  /* does it contain straight segments? */
 
1394
        FT_Pos  ups         = 0;  /* number of upwards segments         */
 
1395
        FT_Pos  downs       = 0;  /* number of downwards segments       */
 
1396
 
 
1397
 
 
1398
        seg = edge->first;
 
1399
 
 
1400
        do
 
1401
        {
 
1402
          FT_Bool  is_serif;
 
1403
 
 
1404
 
 
1405
          /* check for roundness of segment */
 
1406
          if ( seg->flags & AH_EDGE_ROUND )
 
1407
            is_round++;
 
1408
          else
 
1409
            is_straight++;
 
1410
 
 
1411
          /* check for segment direction */
 
1412
          if ( seg->dir == up_dir )
 
1413
            ups   += seg->max_coord-seg->min_coord;
 
1414
          else
 
1415
            downs += seg->max_coord-seg->min_coord;
 
1416
 
 
1417
          /* check for links -- if seg->serif is set, then seg->link must */
 
1418
          /* be ignored                                                   */
 
1419
          is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
 
1420
 
 
1421
          if ( seg->link || is_serif )
 
1422
          {
 
1423
            AH_Edge     edge2;
 
1424
            AH_Segment  seg2;
 
1425
 
 
1426
 
 
1427
            edge2 = edge->link;
 
1428
            seg2  = seg->link;
 
1429
 
 
1430
            if ( is_serif )
 
1431
            {
 
1432
              seg2  = seg->serif;
 
1433
              edge2 = edge->serif;
 
1434
            }
 
1435
 
 
1436
            if ( edge2 )
 
1437
            {
 
1438
              FT_Pos  edge_delta;
 
1439
              FT_Pos  seg_delta;
 
1440
 
 
1441
 
 
1442
              edge_delta = edge->fpos - edge2->fpos;
 
1443
              if ( edge_delta < 0 )
 
1444
                edge_delta = -edge_delta;
 
1445
 
 
1446
              seg_delta = seg->pos - seg2->pos;
 
1447
              if ( seg_delta < 0 )
 
1448
                seg_delta = -seg_delta;
 
1449
 
 
1450
              if ( seg_delta < edge_delta )
 
1451
                edge2 = seg2->edge;
 
1452
            }
 
1453
            else
 
1454
              edge2 = seg2->edge;
 
1455
 
 
1456
#ifdef FT_CONFIG_CHESTER_SERIF
 
1457
            if ( is_serif )
 
1458
            {
 
1459
              edge->serif   = edge2;
 
1460
              edge2->flags |= AH_EDGE_SERIF;
 
1461
            }
 
1462
            else
 
1463
              edge->link  = edge2;
 
1464
#else /* !FT_CONFIG_CHESTER_SERIF */
 
1465
            if ( is_serif )
 
1466
              edge->serif = edge2;
 
1467
            else
 
1468
              edge->link  = edge2;
 
1469
#endif /* !FT_CONFIG_CHESTER_SERIF */
 
1470
          }
 
1471
 
 
1472
          seg = seg->edge_next;
 
1473
 
 
1474
        } while ( seg != edge->first );
 
1475
 
 
1476
        /* set the round/straight flags */
 
1477
        edge->flags = AH_EDGE_NORMAL;
 
1478
 
 
1479
        if ( is_round > 0 && is_round >= is_straight )
 
1480
          edge->flags |= AH_EDGE_ROUND;
 
1481
 
 
1482
        /* set the edge's main direction */
 
1483
        edge->dir = AH_DIR_NONE;
 
1484
 
 
1485
        if ( ups > downs )
 
1486
          edge->dir = up_dir;
 
1487
 
 
1488
        else if ( ups < downs )
 
1489
          edge->dir = -up_dir;
 
1490
 
 
1491
        else if ( ups == downs )
 
1492
          edge->dir = 0;  /* both up and down! */
 
1493
 
 
1494
        /* gets rid of serifs if link is set                */
 
1495
        /* XXX: This gets rid of many unpleasant artefacts! */
 
1496
        /*      Example: the `c' in cour.pfa at size 13     */
 
1497
 
 
1498
        if ( edge->serif && edge->link )
 
1499
          edge->serif = 0;
 
1500
      }
 
1501
 
 
1502
      edges         = outline->vert_edges;
 
1503
      segments      = outline->vert_segments;
 
1504
      segment_limit = segments + outline->num_vsegments;
 
1505
      p_num_edges   = &outline->num_vedges;
 
1506
      up_dir        = AH_DIR_UP;
 
1507
      scale         = outline->x_scale;
 
1508
    }
 
1509
  }
 
1510
 
 
1511
 
 
1512
  /*************************************************************************/
 
1513
  /*                                                                       */
 
1514
  /* <Function>                                                            */
 
1515
  /*    ah_outline_detect_features                                         */
 
1516
  /*                                                                       */
 
1517
  /* <Description>                                                         */
 
1518
  /*    Performs feature detection on a given AH_OutlineRec object.        */
 
1519
  /*                                                                       */
 
1520
  FT_LOCAL_DEF( void )
 
1521
  ah_outline_detect_features( AH_Outline  outline )
 
1522
  {
 
1523
    ah_outline_compute_segments   ( outline );
 
1524
    ah_outline_link_segments      ( outline );
 
1525
    ah_outline_compute_edges      ( outline );
 
1526
    ah_outline_compute_inflections( outline );
 
1527
  }
 
1528
 
 
1529
 
 
1530
  /*************************************************************************/
 
1531
  /*                                                                       */
 
1532
  /* <Function>                                                            */
 
1533
  /*    ah_outline_compute_blue_edges                                      */
 
1534
  /*                                                                       */
 
1535
  /* <Description>                                                         */
 
1536
  /*    Computes the `blue edges' in a given outline (i.e. those that must */
 
1537
  /*    be snapped to a blue zone edge (top or bottom).                    */
 
1538
  /*                                                                       */
 
1539
  FT_LOCAL_DEF( void )
 
1540
  ah_outline_compute_blue_edges( AH_Outline       outline,
 
1541
                                 AH_Face_Globals  face_globals )
 
1542
  {
 
1543
    AH_Edge     edge       = outline->horz_edges;
 
1544
    AH_Edge     edge_limit = edge + outline->num_hedges;
 
1545
    AH_Globals  globals    = &face_globals->design;
 
1546
    FT_Fixed    y_scale    = outline->y_scale;
 
1547
 
 
1548
    FT_Bool     blue_active[AH_BLUE_MAX];
 
1549
 
 
1550
 
 
1551
    /* compute which blue zones are active, i.e. have their scaled */
 
1552
    /* size < 3/4 pixels                                           */
 
1553
    {
 
1554
      AH_Blue  blue;
 
1555
      FT_Bool  check = 0;
 
1556
 
 
1557
 
 
1558
      for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ )
 
1559
      {
 
1560
        FT_Pos  ref, shoot, dist;
 
1561
 
 
1562
 
 
1563
        ref   = globals->blue_refs[blue];
 
1564
        shoot = globals->blue_shoots[blue];
 
1565
        dist  = ref - shoot;
 
1566
        if ( dist < 0 )
 
1567
          dist = -dist;
 
1568
 
 
1569
        blue_active[blue] = 0;
 
1570
 
 
1571
        if ( FT_MulFix( dist, y_scale ) < 48 )
 
1572
        {
 
1573
          blue_active[blue] = 1;
 
1574
          check = 1;
 
1575
        }
 
1576
      }
 
1577
 
 
1578
      /* return immediately if no blue zone is active */
 
1579
      if ( !check )
 
1580
        return;
 
1581
    }
 
1582
 
 
1583
    /* for each horizontal edge search the blue zone which is closest */
 
1584
    for ( ; edge < edge_limit; edge++ )
 
1585
    {
 
1586
      AH_Blue  blue;
 
1587
      FT_Pos*  best_blue = 0;
 
1588
      FT_Pos   best_dist;  /* initial threshold */
 
1589
 
 
1590
 
 
1591
      /* compute the initial threshold as a fraction of the EM size */
 
1592
      best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale );
 
1593
 
 
1594
#ifdef FT_CONFIG_CHESTER_SMALL_F
 
1595
      if ( best_dist > 64 / 2 )
 
1596
        best_dist = 64 / 2;
 
1597
#else
 
1598
      if ( best_dist > 64 / 4 )
 
1599
        best_dist = 64 / 4;
 
1600
#endif
 
1601
 
 
1602
      for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ )
 
1603
      {
 
1604
        /* if it is a top zone, check for right edges -- if it is a bottom */
 
1605
        /* zone, check for left edges                                      */
 
1606
        /*                                                                 */
 
1607
        /* of course, that's for TrueType XXX                              */
 
1608
        FT_Bool  is_top_blue  =
 
1609
                   FT_BOOL( AH_IS_TOP_BLUE( blue ) );
 
1610
        FT_Bool  is_major_dir =
 
1611
                   FT_BOOL( edge->dir == outline->horz_major_dir );
 
1612
 
 
1613
 
 
1614
        if ( !blue_active[blue] )
 
1615
          continue;
 
1616
 
 
1617
        /* if it is a top zone, the edge must be against the major    */
 
1618
        /* direction; if it is a bottom zone, it must be in the major */
 
1619
        /* direction                                                  */
 
1620
        if ( is_top_blue ^ is_major_dir )
 
1621
        {
 
1622
          FT_Pos   dist;
 
1623
          FT_Pos*  blue_pos = globals->blue_refs + blue;
 
1624
 
 
1625
 
 
1626
          /* first of all, compare it to the reference position */
 
1627
          dist = edge->fpos - *blue_pos;
 
1628
          if ( dist < 0 )
 
1629
            dist = -dist;
 
1630
 
 
1631
          dist = FT_MulFix( dist, y_scale );
 
1632
          if ( dist < best_dist )
 
1633
          {
 
1634
            best_dist = dist;
 
1635
            best_blue = blue_pos;
 
1636
          }
 
1637
 
 
1638
          /* now, compare it to the overshoot position if the edge is     */
 
1639
          /* rounded, and if the edge is over the reference position of a */
 
1640
          /* top zone, or under the reference position of a bottom zone   */
 
1641
          if ( edge->flags & AH_EDGE_ROUND && dist != 0 )
 
1642
          {
 
1643
            FT_Bool  is_under_ref = FT_BOOL( edge->fpos < *blue_pos );
 
1644
 
 
1645
 
 
1646
            if ( is_top_blue ^ is_under_ref )
 
1647
            {
 
1648
              blue_pos = globals->blue_shoots + blue;
 
1649
              dist = edge->fpos - *blue_pos;
 
1650
              if ( dist < 0 )
 
1651
                dist = -dist;
 
1652
 
 
1653
              dist = FT_MulFix( dist, y_scale );
 
1654
              if ( dist < best_dist )
 
1655
              {
 
1656
                best_dist = dist;
 
1657
                best_blue = blue_pos;
 
1658
              }
 
1659
            }
 
1660
          }
 
1661
        }
 
1662
      }
 
1663
 
 
1664
      if ( best_blue )
 
1665
        edge->blue_edge = best_blue;
 
1666
    }
 
1667
  }
 
1668
 
 
1669
 
 
1670
  /*************************************************************************/
 
1671
  /*                                                                       */
 
1672
  /* <Function>                                                            */
 
1673
  /*    ah_outline_scale_blue_edges                                        */
 
1674
  /*                                                                       */
 
1675
  /* <Description>                                                         */
 
1676
  /*    This function must be called before hinting in order to re-adjust  */
 
1677
  /*    the contents of the detected edges (basically change the `blue     */
 
1678
  /*    edge' pointer from `design units' to `scaled ones').               */
 
1679
  /*                                                                       */
 
1680
  FT_LOCAL_DEF( void )
 
1681
  ah_outline_scale_blue_edges( AH_Outline       outline,
 
1682
                               AH_Face_Globals  globals )
 
1683
  {
 
1684
    AH_Edge  edge       = outline->horz_edges;
 
1685
    AH_Edge  edge_limit = edge + outline->num_hedges;
 
1686
    FT_Pos   delta;
 
1687
 
 
1688
 
 
1689
    delta = globals->scaled.blue_refs - globals->design.blue_refs;
 
1690
 
 
1691
    for ( ; edge < edge_limit; edge++ )
 
1692
    {
 
1693
      if ( edge->blue_edge )
 
1694
        edge->blue_edge += delta;
 
1695
    }
 
1696
  }
 
1697
 
 
1698
 
 
1699
/* END */