~ubuntu-branches/ubuntu/wily/hedgewars/wily

« back to all changes in this revision

Viewing changes to misc/libfreetype/src/autofit/aflatin.c

  • Committer: Package Import Robot
  • Author(s): Dmitry E. Oboukhov
  • Date: 2011-09-23 10:16:55 UTC
  • mfrom: (1.2.11 upstream)
  • Revision ID: package-import@ubuntu.com-20110923101655-3977th2gc5n0a3pv
Tags: 0.9.16-1
* New upstream version.
 + Downloadable content! Simply click to install any content.
   New voices, hats, maps, themes, translations, music, scripts...
   Hedgewars is now more customisable than ever before! As time goes
   by we will be soliciting community content to feature on this page,
   so remember to check it from time to time. If you decide you want
   to go back to standard Hedgewars, just remove the Data directory
   from your Hedgewars config directory.
 + 3-D rendering! Diorama-like rendering of the game in a variety
   of 3D modes. Let us know which ones work best for you, we didn't
   really have the equipment to test them all.
 + Resizable game window.
 + New utilities! The Time Box will remove one of your hedgehogs
   from the game for a while, protecting from attack until it returns,
   somewhere else on the map. Land spray will allow you to build bridges,
   seal up holes, or just make life unpleasant for your enemies.
 + New single player: Bamboo Thicket, That Sinking Feeling, Newton and
   the Tree and multi-player: The Specialists, Space Invaders,
   Racer - scripts! And a ton more script hooks for scripters
 + New twists on old weapons. Drill strike, seduction and fire have
   been adjusted. Defective mines have been added, rope can attach to
   hogs/crates/barrels again, grenades now have variable bounce (use
   precise key + 1-5). Portal gun is now more usable in flight and
   all game actions are a lot faster.
 + New theme - Golf, dozens of new community hats and a new
   localised Default voice, Ukranian.

Show diffs side-by-side

added added

removed removed

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