1a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/***************************************************************************/
2a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*                                                                         */
3a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  cf2hints.c                                                             */
4a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*                                                                         */
5a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*    Adobe's code for handling CFF hints (body).                          */
6a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*                                                                         */
7a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
8a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*                                                                         */
9a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  This software, and all works of authorship, whether in source or       */
10a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  object code form as indicated by the copyright notice(s) included      */
11a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  herein (collectively, the "Work") is made available, and may only be   */
12a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  used, modified, and distributed under the FreeType Project License,    */
13a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
14a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  FreeType Project License, each contributor to the Work hereby grants   */
15a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  to any individual or legal entity exercising permissions granted by    */
16a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  the FreeType Project License and this section (hereafter, "You" or     */
17a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
18a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  royalty-free, irrevocable (except as stated in this section) patent    */
19a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  license to make, have made, use, offer to sell, sell, import, and      */
20a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  otherwise transfer the Work, where such license applies only to those  */
21a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  patent claims licensable by such contributor that are necessarily      */
22a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  infringed by their contribution(s) alone or by combination of their    */
23a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  contribution(s) with the Work to which such contribution(s) was        */
24a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  submitted.  If You institute patent litigation against any entity      */
25a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
26a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  the Work or a contribution incorporated within the Work constitutes    */
27a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  direct or contributory patent infringement, then any patent licenses   */
28a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  granted to You under this License for that Work shall terminate as of  */
29a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  the date such litigation is filed.                                     */
30a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*                                                                         */
31a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  By using, modifying, or distributing the Work you indicate that you    */
32a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  have read and understood the terms and conditions of the               */
33a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  FreeType Project License as well as those provided in this section,    */
34a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*  and you accept them fully.                                             */
35a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/*                                                                         */
36a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/***************************************************************************/
37a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
38a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
39a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#include "cf2ft.h"
40a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#include FT_INTERNAL_DEBUG_H
41a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
42a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#include "cf2glue.h"
43a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#include "cf2font.h"
44a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#include "cf2hints.h"
45a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#include "cf2intrp.h"
46a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
47a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
48a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*************************************************************************/
49a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*                                                                       */
50a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
51a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
52a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* messages during execution.                                            */
53a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*                                                                       */
54a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#undef  FT_COMPONENT
55a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#define FT_COMPONENT  trace_cf2hints
56a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
57a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
58a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  typedef struct  CF2_HintMoveRec_
59a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
60a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    size_t     j;          /* index of upper hint map edge   */
61a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Fixed  moveUp;     /* adjustment to optimum position */
62a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
63a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  } CF2_HintMoveRec, *CF2_HintMove;
64a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
65a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
66a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* Compute angular momentum for winding order detection.  It is called */
67a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* for all lines and curves, but not necessarily in element order.     */
68a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static CF2_Int
69a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_getWindingMomentum( CF2_Fixed  x1,
70a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          CF2_Fixed  y1,
71a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          CF2_Fixed  x2,
72a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          CF2_Fixed  y2 )
73a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
74a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* cross product of pt1 position from origin with pt2 position from  */
75a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* pt1; we reduce the precision so that the result fits into 32 bits */
76a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
77a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return ( x1 >> 16 ) * ( ( y2 - y1 ) >> 16 ) -
78a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang           ( y1 >> 16 ) * ( ( x2 - x1 ) >> 16 );
79a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
80a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
81a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
82a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*
83a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Construct from a StemHint; this is used as a parameter to
84a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * `cf2_blues_capture'.
85a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * `hintOrigin' is the character space displacement of a seac accent.
86a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Adjust stem hint for darkening here.
87a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
88a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   */
89a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static void
90a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_init( CF2_Hint            hint,
91a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 const CF2_ArrStack  stemHintArray,
92a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 size_t              indexStemHint,
93a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 const CF2_Font      font,
94a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 CF2_Fixed           hintOrigin,
95a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 CF2_Fixed           scale,
96a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 FT_Bool             bottom )
97a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
98a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Fixed               width;
99a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    const CF2_StemHintRec*  stemHint;
100a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
101a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
102a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ZERO( hint );
103a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
104a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    stemHint = (const CF2_StemHintRec*)cf2_arrstack_getPointer(
105a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                         stemHintArray,
106a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                         indexStemHint );
107a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
108a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    width = stemHint->max - stemHint->min;
109a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
110a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( width == cf2_intToFixed( -21 ) )
111a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
112a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* ghost bottom */
113a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
114a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( bottom )
115a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
116a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->csCoord = stemHint->max;
117a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->flags   = CF2_GhostBottom;
118a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
119a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
120a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->flags = 0;
121a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
122a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
123a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else if ( width == cf2_intToFixed( -20 ) )
124a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
125a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* ghost top */
126a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
127a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( bottom )
128a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->flags = 0;
129a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
130a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
131a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->csCoord = stemHint->min;
132a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->flags   = CF2_GhostTop;
133a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
134a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
135a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
136a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else if ( width < 0 )
137a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
138a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* inverted pair */
139a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
140a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /*
141a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       * Hints with negative widths were produced by an early version of a
142a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       * non-Adobe font tool.  The Type 2 spec allows edge (ghost) hints
143a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       * with negative widths, but says
144a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       *
145a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       *   All other negative widths have undefined meaning.
146a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       *
147a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       * CoolType has a silent workaround that negates the hint width; for
148a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       * permissive mode, we do the same here.
149a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       *
150a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       * Note: Such fonts cannot use ghost hints, but should otherwise work.
151a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       * Note: Some poor hints in our faux fonts can produce negative
152a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       *       widths at some blends.  For example, see a light weight of
153a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       *       `u' in ASerifMM.
154a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       *
155a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang       */
156a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( bottom )
157a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
158a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->csCoord = stemHint->max;
159a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->flags   = CF2_PairBottom;
160a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
161a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
162a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
163a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->csCoord = stemHint->min;
164a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->flags   = CF2_PairTop;
165a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
166a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
167a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
168a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else
169a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
170a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* normal pair */
171a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
172a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( bottom )
173a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
174a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->csCoord = stemHint->min;
175a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->flags   = CF2_PairBottom;
176a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
177a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
178a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
179a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->csCoord = stemHint->max;
180a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->flags   = CF2_PairTop;
181a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
182a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
183a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
184a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* Now that ghost hints have been detected, adjust this edge for      */
185a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* darkening.  Bottoms are not changed; tops are incremented by twice */
186a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* `darkenY'.                                                         */
187a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( cf2_hint_isTop( hint ) )
188a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      hint->csCoord += 2 * font->darkenY;
189a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
190a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hint->csCoord += hintOrigin;
191a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hint->scale    = scale;
192a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hint->index    = indexStemHint;   /* index in original stem hint array */
193a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
194a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* if original stem hint has been used, use the same position */
195a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( hint->flags != 0 && stemHint->used )
196a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
197a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( cf2_hint_isTop( hint ) )
198a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->dsCoord = stemHint->maxDS;
199a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
200a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hint->dsCoord = stemHint->minDS;
201a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
202a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hint_lock( hint );
203a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
204a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else
205a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      hint->dsCoord = FT_MulFix( hint->csCoord, scale );
206a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
207a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
208a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
209a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* initialize an invalid hint map element */
210a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static void
211a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_initZero( CF2_Hint  hint )
212a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
213a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ZERO( hint );
214a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
215a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
216a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
217a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( FT_Bool )
218a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_isValid( const CF2_Hint  hint )
219a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
220a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return hint->flags != 0;
221a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
222a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
223a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
224a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static FT_Bool
225a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_isPair( const CF2_Hint  hint )
226a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
227a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return ( hint->flags                      &
228a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             ( CF2_PairBottom | CF2_PairTop ) ) != 0;
229a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
230a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
231a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
232a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static FT_Bool
233a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_isPairTop( const CF2_Hint  hint )
234a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
235a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return ( hint->flags & CF2_PairTop ) != 0;
236a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
237a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
238a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
239a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( FT_Bool )
240a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_isTop( const CF2_Hint  hint )
241a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
242a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return ( hint->flags                    &
243a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             ( CF2_PairTop | CF2_GhostTop ) ) != 0;
244a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
245a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
246a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
247a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( FT_Bool )
248a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_isBottom( const CF2_Hint  hint )
249a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
250a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return ( hint->flags                          &
251a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             ( CF2_PairBottom | CF2_GhostBottom ) ) != 0;
252a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
253a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
254a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
255a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static FT_Bool
256a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_isLocked( const CF2_Hint  hint )
257a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
258a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return ( hint->flags & CF2_Locked ) != 0;
259a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
260a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
261a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
262a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static FT_Bool
263a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_isSynthetic( const CF2_Hint  hint )
264a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
265a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return ( hint->flags & CF2_Synthetic ) != 0;
266a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
267a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
268a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
269a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
270a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hint_lock( CF2_Hint  hint )
271a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
272a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hint->flags |= CF2_Locked;
273a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
274a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
275a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
276a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
277a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hintmap_init( CF2_HintMap   hintmap,
278a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                    CF2_Font      font,
279a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                    CF2_HintMap   initialMap,
280a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                    CF2_ArrStack  hintMoves,
281a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                    CF2_Fixed     scale )
282a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
283a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ZERO( hintmap );
284a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
285a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* copy parameters from font instance */
286a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hintmap->hinted         = font->hinted;
287a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hintmap->scale          = scale;
288a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hintmap->font           = font;
289a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hintmap->initialHintMap = initialMap;
290a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* will clear in `cf2_hintmap_adjustHints' */
291a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hintmap->hintMoves      = hintMoves;
292a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
293a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
294a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
295a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static FT_Bool
296a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hintmap_isValid( const CF2_HintMap  hintmap )
297a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
298a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return hintmap->isValid;
299a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
300a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
301a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
302a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* transform character space coordinate to device space using hint map */
303a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static CF2_Fixed
304a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hintmap_map( CF2_HintMap  hintmap,
305a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                   CF2_Fixed    csCoord )
306a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
307a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ASSERT( hintmap->isValid );  /* must call Build before Map */
308a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES );
309a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
310a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( hintmap->count == 0 || ! hintmap->hinted )
311a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
312a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* there are no hints; use uniform scale and zero offset */
313a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      return FT_MulFix( csCoord, hintmap->scale );
314a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
315a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else
316a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
317a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* start linear search from last hit */
318a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      CF2_UInt  i = hintmap->lastIndex;
319a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
320a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
321a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* search up */
322a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      while ( i < hintmap->count - 1                  &&
323a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang              csCoord >= hintmap->edge[i + 1].csCoord )
324a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        i += 1;
325a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
326a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* search down */
327a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      while ( i > 0 && csCoord < hintmap->edge[i].csCoord )
328a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        i -= 1;
329a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
330a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      hintmap->lastIndex = i;
331a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
332a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( i == 0 && csCoord < hintmap->edge[0].csCoord )
333a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
334a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* special case for points below first edge: use uniform scale */
335a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        return FT_MulFix( csCoord - hintmap->edge[0].csCoord,
336a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          hintmap->scale ) +
337a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 hintmap->edge[0].dsCoord;
338a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
339a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
340a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
341a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /*
342a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         * Note: entries with duplicate csCoord are allowed.
343a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         * Use edge[i], the highest entry where csCoord >= entry[i].csCoord
344a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         */
345a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        return FT_MulFix( csCoord - hintmap->edge[i].csCoord,
346a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          hintmap->edge[i].scale ) +
347a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 hintmap->edge[i].dsCoord;
348a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
349a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
350a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
351a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
352a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
353a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*
354a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * This hinting policy moves a hint pair in device space so that one of
355a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * its two edges is on a device pixel boundary (its fractional part is
356a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * zero).  `cf2_hintmap_insertHint' guarantees no overlap in CS
357a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * space.  Ensure here that there is no overlap in DS.
358a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
359a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * In the first pass, edges are adjusted relative to adjacent hints.
360a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Those that are below have already been adjusted.  Those that are
361a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * above have not yet been adjusted.  If a hint above blocks an
362a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * adjustment to an optimal position, we will try again in a second
363a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * pass.  The second pass is top-down.
364a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
365a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   */
366a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
367a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static void
368a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hintmap_adjustHints( CF2_HintMap  hintmap )
369a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
370a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    size_t  i, j;
371a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
372a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
373a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_arrstack_clear( hintmap->hintMoves );      /* working storage */
374a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
375a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /*
376a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * First pass is bottom-up (font hint order) without look-ahead.
377a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Locked edges are already adjusted.
378a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Unlocked edges begin with dsCoord from `initialHintMap'.
379a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Save edges that are not optimally adjusted in `hintMoves' array,
380a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * and process them in second pass.
381a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     */
382a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
383a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    for ( i = 0; i < hintmap->count; i++ )
384a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
385a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_Bool  isPair = cf2_hint_isPair( &hintmap->edge[i] );
386a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
387a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
388a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* index of upper edge (same value for ghost hint) */
389a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      j = isPair ? i + 1 : i;
390a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
391a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( j < hintmap->count );
392a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( cf2_hint_isValid( &hintmap->edge[i] ) );
393a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( cf2_hint_isValid( &hintmap->edge[j] ) );
394a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( cf2_hint_isLocked( &hintmap->edge[i] ) ==
395a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                   cf2_hint_isLocked( &hintmap->edge[j] ) );
396a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
397a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( !cf2_hint_isLocked( &hintmap->edge[i] ) )
398a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
399a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* hint edge is not locked, we can adjust it */
400a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  fracDown = cf2_fixedFraction( hintmap->edge[i].dsCoord );
401a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  fracUp   = cf2_fixedFraction( hintmap->edge[j].dsCoord );
402a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
403a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* calculate all four possibilities; moves down are negative */
404a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  downMoveDown = 0 - fracDown;
405a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  upMoveDown   = 0 - fracUp;
406a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  downMoveUp   = fracDown == 0
407a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                    ? 0
408a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                    : cf2_intToFixed( 1 ) - fracDown;
409a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  upMoveUp     = fracUp == 0
410a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                    ? 0
411a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                    : cf2_intToFixed( 1 ) - fracUp;
412a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
413a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* smallest move up */
414a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  moveUp   = FT_MIN( downMoveUp, upMoveUp );
415a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* smallest move down */
416a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  moveDown = FT_MAX( downMoveDown, upMoveDown );
417a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
418a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* final amount to move edge or edge pair */
419a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  move;
420a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
421a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  downMinCounter = CF2_MIN_COUNTER;
422a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  upMinCounter   = CF2_MIN_COUNTER;
423a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        FT_Bool    saveEdge       = FALSE;
424a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
425a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
426a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* minimum counter constraint doesn't apply when adjacent edges */
427a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* are synthetic                                                */
428a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* TODO: doesn't seem a big effect; for now, reduce the code    */
429a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#if 0
430a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( i == 0                                        ||
431a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             cf2_hint_isSynthetic( &hintmap->edge[i - 1] ) )
432a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          downMinCounter = 0;
433a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
434a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( j >= hintmap->count - 1                       ||
435a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             cf2_hint_isSynthetic( &hintmap->edge[j + 1] ) )
436a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          upMinCounter = 0;
437a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#endif
438a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
439a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* is there room to move up?                                    */
440a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* there is if we are at top of array or the next edge is at or */
441a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* beyond proposed move up?                                     */
442a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( j >= hintmap->count - 1                            ||
443a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             hintmap->edge[j + 1].dsCoord >=
444a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               hintmap->edge[j].dsCoord + moveUp + upMinCounter )
445a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
446a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* there is room to move up; is there also room to move down? */
447a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          if ( i == 0                                                 ||
448a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               hintmap->edge[i - 1].dsCoord <=
449a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 hintmap->edge[i].dsCoord + moveDown - downMinCounter )
450a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          {
451a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            /* move smaller absolute amount */
452a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            move = ( -moveDown < moveUp ) ? moveDown : moveUp;  /* optimum */
453a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          }
454a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          else
455a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            move = moveUp;
456a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
457a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else
458a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
459a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* is there room to move down? */
460a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          if ( i == 0                                                 ||
461a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               hintmap->edge[i - 1].dsCoord <=
462a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 hintmap->edge[i].dsCoord + moveDown - downMinCounter )
463a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          {
464a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            move     = moveDown;
465a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            saveEdge = moveUp < -moveDown;  /* true if non-optimum move */
466a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          }
467a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          else
468a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          {
469a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            /* no room to move either way without overlapping or reducing */
470a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            /* the counter too much                                       */
471a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            move     = 0;
472a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            saveEdge = TRUE;
473a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          }
474a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
475a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
476a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* Identify non-moves and moves down that aren't optimal, and save */
477a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* them for second pass.                                           */
478a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* Do this only if there is an unlocked edge above (which could    */
479a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* possibly move).                                                 */
480a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( saveEdge                                    &&
481a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             j < hintmap->count - 1                      &&
482a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             !cf2_hint_isLocked( &hintmap->edge[j + 1] ) )
483a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
484a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          CF2_HintMoveRec  savedMove;
485a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
486a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
487a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          savedMove.j      = j;
488a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* desired adjustment in second pass */
489a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          savedMove.moveUp = moveUp - move;
490a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
491a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          cf2_arrstack_push( hintmap->hintMoves, &savedMove );
492a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
493a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
494a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* move the edge(s) */
495a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hintmap->edge[i].dsCoord += move;
496a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( isPair )
497a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          hintmap->edge[j].dsCoord += move;
498a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
499a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
500a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* assert there are no overlaps in device space */
501a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( i == 0                                                   ||
502a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 hintmap->edge[i - 1].dsCoord <= hintmap->edge[i].dsCoord );
503a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( i < j                                                ||
504a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                 hintmap->edge[i].dsCoord <= hintmap->edge[j].dsCoord );
505a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
506a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* adjust the scales, avoiding divide by zero */
507a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( i > 0 )
508a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
509a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( hintmap->edge[i].csCoord != hintmap->edge[i - 1].csCoord )
510a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          hintmap->edge[i - 1].scale =
511a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            FT_DivFix(
512a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang              hintmap->edge[i].dsCoord - hintmap->edge[i - 1].dsCoord,
513a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang              hintmap->edge[i].csCoord - hintmap->edge[i - 1].csCoord );
514a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
515a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
516a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( isPair )
517a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
518a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( hintmap->edge[j].csCoord != hintmap->edge[j - 1].csCoord )
519a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          hintmap->edge[j - 1].scale =
520a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            FT_DivFix(
521a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang              hintmap->edge[j].dsCoord - hintmap->edge[j - 1].dsCoord,
522a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang              hintmap->edge[j].csCoord - hintmap->edge[j - 1].csCoord );
523a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
524a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        i += 1;     /* skip upper edge on next loop */
525a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
526a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
527a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
528a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* second pass tries to move non-optimal hints up, in case there is */
529a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* room now                                                         */
530a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    for ( i = cf2_arrstack_size( hintmap->hintMoves ); i > 0; i-- )
531a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
532a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      CF2_HintMove  hintMove = (CF2_HintMove)
533a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      cf2_arrstack_getPointer( hintmap->hintMoves, i - 1 );
534a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
535a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
536a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      j = hintMove->j;
537a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
538a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* this was tested before the push, above */
539a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( j < hintmap->count - 1 );
540a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
541a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* is there room to move up? */
542a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( hintmap->edge[j + 1].dsCoord >=
543a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             hintmap->edge[j].dsCoord + hintMove->moveUp + CF2_MIN_COUNTER )
544a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
545a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* there is more room now, move edge up */
546a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hintmap->edge[j].dsCoord += hintMove->moveUp;
547a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
548a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( cf2_hint_isPair( &hintmap->edge[j] ) )
549a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
550a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          FT_ASSERT( j > 0 );
551a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          hintmap->edge[j - 1].dsCoord += hintMove->moveUp;
552a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
553a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
554a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
555a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
556a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
557a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
558a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* insert hint edges into map, sorted by csCoord */
559a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static void
560a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hintmap_insertHint( CF2_HintMap  hintmap,
561a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          CF2_Hint     bottomHintEdge,
562a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          CF2_Hint     topHintEdge )
563a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
564a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_UInt  indexInsert;
565a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
566a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* set default values, then check for edge hints */
567a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Bool   isPair         = TRUE;
568a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Hint  firstHintEdge  = bottomHintEdge;
569a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Hint  secondHintEdge = topHintEdge;
570a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
571a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
572a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* one or none of the input params may be invalid when dealing with */
573a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* edge hints; at least one edge must be valid                      */
574a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ASSERT( cf2_hint_isValid( bottomHintEdge ) ||
575a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               cf2_hint_isValid( topHintEdge )    );
576a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
577a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* determine how many and which edges to insert */
578a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( !cf2_hint_isValid( bottomHintEdge ) )
579a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
580a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* insert only the top edge */
581a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      firstHintEdge = topHintEdge;
582a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      isPair        = FALSE;
583a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
584a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else if ( !cf2_hint_isValid( topHintEdge ) )
585a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
586a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* insert only the bottom edge */
587a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      isPair = FALSE;
588a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
589a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
590a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* paired edges must be in proper order */
591a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ASSERT( !isPair                                         ||
592a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               topHintEdge->csCoord >= bottomHintEdge->csCoord );
593a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
594a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* linear search to find index value of insertion point */
595a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    indexInsert = 0;
596a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    for ( ; indexInsert < hintmap->count; indexInsert++ )
597a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
598a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( hintmap->edge[indexInsert].csCoord > firstHintEdge->csCoord )
599a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        break;
600a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
601a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
602a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /*
603a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Discard any hints that overlap in character space.  Most often,
604a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * this is while building the initial map, but in theory, it can also
605a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * occur because of darkening.
606a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *
607a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     */
608a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( indexInsert < hintmap->count )
609a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
610a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* we are inserting before an existing edge:              */
611a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* verify that a new pair does not straddle the next edge */
612a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( isPair                                                       &&
613a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang           hintmap->edge[indexInsert].csCoord < secondHintEdge->csCoord )
614a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        return; /* ignore overlapping stem hint */
615a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
616a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* verify that we are not inserting between paired edges */
617a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) )
618a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        return; /* ignore overlapping stem hint */
619a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
620a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
621a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* recompute device space locations using initial hint map */
622a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( cf2_hintmap_isValid( hintmap->initialHintMap ) &&
623a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         !cf2_hint_isLocked( firstHintEdge )            )
624a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
625a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( isPair )
626a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
627a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* Use hint map to position the center of stem, and nominal scale */
628a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* to position the two edges.  This preserves the stem width.     */
629a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  midpoint  = cf2_hintmap_map(
630a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 hintmap->initialHintMap,
631a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 ( secondHintEdge->csCoord +
632a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                   firstHintEdge->csCoord ) / 2 );
633a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_Fixed  halfWidth = FT_MulFix(
634a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 ( secondHintEdge->csCoord -
635a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                   firstHintEdge->csCoord ) / 2,
636a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 hintmap->scale );
637a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
638a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
639a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        firstHintEdge->dsCoord  = midpoint - halfWidth;
640a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        secondHintEdge->dsCoord = midpoint + halfWidth;
641a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
642a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
643a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap,
644a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                                  firstHintEdge->csCoord );
645a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
646a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
647a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* discard any hints that overlap in device space; this can occur */
648a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* because locked hints have been moved to align with blue zones  */
649a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( indexInsert > 0 )
650a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
651a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* we are inserting after an existing edge */
652a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord )
653a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        return;
654a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
655a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
656a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( indexInsert < hintmap->count )
657a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
658a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* we are inserting before an existing edge */
659a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( isPair )
660a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
661a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( secondHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord )
662a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          return;
663a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
664a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
665a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
666a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( firstHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord )
667a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          return;
668a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
669a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
670a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
671a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* make room to insert */
672a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
673a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      CF2_Int  iSrc = hintmap->count - 1;
674a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      CF2_Int  iDst = isPair ? hintmap->count + 1 : hintmap->count;
675a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
676a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      CF2_Int  count = hintmap->count - indexInsert;
677a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
678a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
679a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( iDst >= CF2_MAX_HINT_EDGES )
680a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
681a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        FT_TRACE4(( "cf2_hintmap_insertHint: too many hintmaps\n" ));
682a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        return;
683a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
684a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
685a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      while ( count-- )
686a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hintmap->edge[iDst--] = hintmap->edge[iSrc--];
687a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
688a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* insert first edge */
689a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      hintmap->edge[indexInsert] = *firstHintEdge;         /* copy struct */
690a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      hintmap->count += 1;
691a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
692a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( isPair )
693a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
694a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* insert second edge */
695a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hintmap->edge[indexInsert + 1] = *secondHintEdge;  /* copy struct */
696a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        hintmap->count                += 1;
697a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
698a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
699a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
700a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return;
701a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
702a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
703a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
704a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*
705a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Build a map from hints and mask.
706a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
707a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * This function may recur one level if `hintmap->initialHintMap' is not yet
708a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * valid.
709a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * If `initialMap' is true, simply build initial map.
710a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
711a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Synthetic hints are used in two ways.  A hint at zero is inserted, if
712a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * needed, in the initial hint map, to prevent translations from
713a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * propagating across the origin.  If synthetic em box hints are enabled
714a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * for ideographic dictionaries, then they are inserted in all hint
715a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * maps, including the initial one.
716a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
717a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   */
718a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
719a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_hintmap_build( CF2_HintMap   hintmap,
720a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                     CF2_ArrStack  hStemHintArray,
721a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                     CF2_ArrStack  vStemHintArray,
722a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                     CF2_HintMask  hintMask,
723a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                     CF2_Fixed     hintOrigin,
724a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                     FT_Bool       initialMap )
725a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
726a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Byte*  maskPtr;
727a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
728a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Font         font = hintmap->font;
729a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_HintMaskRec  tempHintMask;
730a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
731a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    size_t   bitCount, i;
732a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Byte  maskByte;
733a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
734a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
735a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* check whether initial map is constructed */
736a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) )
737a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
738a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* make recursive call with initialHintMap and temporary mask; */
739a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* temporary mask will get all bits set, below */
740a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hintmask_init( &tempHintMask, hintMask->error );
741a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hintmap_build( hintmap->initialHintMap,
742a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         hStemHintArray,
743a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         vStemHintArray,
744a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         &tempHintMask,
745a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         hintOrigin,
746a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         TRUE );
747a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
748a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
749a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( !cf2_hintmask_isValid( hintMask ) )
750a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
751a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* without a hint mask, assume all hints are active */
752a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hintmask_setAll( hintMask,
753a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                           cf2_arrstack_size( hStemHintArray ) +
754a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                             cf2_arrstack_size( vStemHintArray ) );
755a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
756a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
757a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* begin by clearing the map */
758a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hintmap->count     = 0;
759a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hintmap->lastIndex = 0;
760a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
761a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* make a copy of the hint mask so we can modify it */
762a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    tempHintMask = *hintMask;
763a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    maskPtr      = cf2_hintmask_getMaskPtr( &tempHintMask );
764a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
765a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* use the hStem hints only, which are first in the mask */
766a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* TODO: compare this to cffhintmaskGetBitCount */
767a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    bitCount = cf2_arrstack_size( hStemHintArray );
768a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
769a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* synthetic embox hints get highest priority */
770a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( font->blues.doEmBoxHints )
771a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
772a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      CF2_HintRec  dummy;
773a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
774a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
775a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hint_initZero( &dummy );   /* invalid hint map element */
776a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
777a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* ghost bottom */
778a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hintmap_insertHint( hintmap,
779a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                              &font->blues.emBoxBottomEdge,
780a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                              &dummy );
781a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* ghost top */
782a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hintmap_insertHint( hintmap,
783a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                              &dummy,
784a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                              &font->blues.emBoxTopEdge );
785a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
786a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
787a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* insert hints captured by a blue zone or already locked (higher */
788a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* priority)                                                      */
789a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    for ( i = 0, maskByte = 0x80; i < bitCount; i++ )
790a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
791a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( maskByte & *maskPtr )
792a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
793a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* expand StemHint into two `CF2_Hint' elements */
794a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_HintRec  bottomHintEdge, topHintEdge;
795a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
796a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
797a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        cf2_hint_init( &bottomHintEdge,
798a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       hStemHintArray,
799a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       i,
800a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       font,
801a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       hintOrigin,
802a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       hintmap->scale,
803a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       TRUE /* bottom */ );
804a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        cf2_hint_init( &topHintEdge,
805a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       hStemHintArray,
806a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       i,
807a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       font,
808a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       hintOrigin,
809a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       hintmap->scale,
810a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       FALSE /* top */ );
811a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
812a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( cf2_hint_isLocked( &bottomHintEdge ) ||
813a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             cf2_hint_isLocked( &topHintEdge )    ||
814a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             cf2_blues_capture( &font->blues,
815a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                &bottomHintEdge,
816a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                &topHintEdge )   )
817a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
818a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* insert captured hint into map */
819a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge );
820a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
821a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *maskPtr &= ~maskByte;      /* turn off the bit for this hint */
822a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
823a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
824a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
825a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( ( i & 7 ) == 7 )
826a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
827a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* move to next mask byte */
828a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        maskPtr++;
829a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        maskByte = 0x80;
830a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
831a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
832a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        maskByte >>= 1;
833a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
834a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
835a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* initial hint map includes only captured hints plus maybe one at 0 */
836a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
837a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /*
838a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * TODO: There is a problem here because we are trying to build a
839a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       single hint map containing all captured hints.  It is
840a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       possible for there to be conflicts between captured hints,
841a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       either because of darkening or because the hints are in
842a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       separate hint zones (we are ignoring hint zones for the
843a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       initial map).  An example of the latter is MinionPro-Regular
844a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       v2.030 glyph 883 (Greek Capital Alpha with Psili) at 15ppem.
845a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       A stem hint for the psili conflicts with the top edge hint
846a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       for the base character.  The stem hint gets priority because
847a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       of its sort order.  In glyph 884 (Greek Capital Alpha with
848a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       Psili and Oxia), the top of the base character gets a stem
849a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       hint, and the psili does not.  This creates different initial
850a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       maps for the two glyphs resulting in different renderings of
851a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       the base character.  Will probably defer this either as not
852a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       worth the cost or as a font bug.  I don't think there is any
853a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       good reason for an accent to be captured by an alignment
854a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       zone.  -darnold 2/12/10
855a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     */
856a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
857a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( initialMap )
858a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
859a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* Apply a heuristic that inserts a point for (0,0), unless it's     */
860a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* already covered by a mapping.  This locks the baseline for glyphs */
861a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* that have no baseline hints.                                      */
862a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
863a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( hintmap->count == 0                           ||
864a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang           hintmap->edge[0].csCoord > 0                  ||
865a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang           hintmap->edge[hintmap->count - 1].csCoord < 0 )
866a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
867a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* all edges are above 0 or all edges are below 0; */
868a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* construct a locked edge hint at 0               */
869a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
870a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        CF2_HintRec  edge, invalid;
871a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
872a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
873a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        cf2_hint_initZero( &edge );
874a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
875a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        edge.flags = CF2_GhostBottom |
876a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                     CF2_Locked      |
877a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                     CF2_Synthetic;
878a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        edge.scale = hintmap->scale;
879a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
880a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        cf2_hint_initZero( &invalid );
881a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        cf2_hintmap_insertHint( hintmap, &edge, &invalid );
882a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
883a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
884a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else
885a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
886a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* insert remaining hints */
887a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
888a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask );
889a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
890a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      for ( i = 0, maskByte = 0x80; i < bitCount; i++ )
891a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
892a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( maskByte & *maskPtr )
893a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
894a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          CF2_HintRec  bottomHintEdge, topHintEdge;
895a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
896a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
897a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          cf2_hint_init( &bottomHintEdge,
898a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         hStemHintArray,
899a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         i,
900a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         font,
901a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         hintOrigin,
902a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         hintmap->scale,
903a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         TRUE /* bottom */ );
904a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          cf2_hint_init( &topHintEdge,
905a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         hStemHintArray,
906a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         i,
907a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         font,
908a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         hintOrigin,
909a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         hintmap->scale,
910a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         FALSE /* top */ );
911a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
912a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge );
913a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
914a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
915a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( ( i & 7 ) == 7 )
916a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
917a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* move to next mask byte */
918a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          maskPtr++;
919a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          maskByte = 0x80;
920a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
921a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else
922a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          maskByte >>= 1;
923a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
924a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
925a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
926a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /*
927a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Note: The following line is a convenient place to break when
928a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       debugging hinting.  Examine `hintmap->edge' for the list of
929a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       enabled hints, then step over the call to see the effect of
930a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       adjustment.  We stop here first on the recursive call that
931a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       creates the initial map, and then on each counter group and
932a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *       hint zone.
933a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     */
934a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
935a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* adjust positions of hint edges that are not locked to blue zones */
936a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_hintmap_adjustHints( hintmap );
937a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
938a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* save the position of all hints that were used in this hint map; */
939a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* if we use them again, we'll locate them in the same position    */
940a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( !initialMap )
941a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
942a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      for ( i = 0; i < hintmap->count; i++ )
943a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
944a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( !cf2_hint_isSynthetic( &hintmap->edge[i] ) )
945a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
946a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* Note: include both valid and invalid edges            */
947a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* Note: top and bottom edges are copied back separately */
948a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          CF2_StemHint  stemhint = (CF2_StemHint)
949a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          cf2_arrstack_getPointer( hStemHintArray,
950a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                                   hintmap->edge[i].index );
951a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
952a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
953a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          if ( cf2_hint_isTop( &hintmap->edge[i] ) )
954a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            stemhint->maxDS = hintmap->edge[i].dsCoord;
955a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          else
956a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang            stemhint->minDS = hintmap->edge[i].dsCoord;
957a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
958a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          stemhint->used = TRUE;
959a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
960a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
961a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
962a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
963a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* hint map is ready to use */
964a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    hintmap->isValid = TRUE;
965a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
966a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* remember this mask has been used */
967a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_hintmask_setNew( hintMask, FALSE );
968a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
969a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
970a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
971a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
972a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_init( CF2_GlyphPath         glyphpath,
973a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      CF2_Font              font,
974a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      CF2_OutlineCallbacks  callbacks,
975a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      CF2_Fixed             scaleY,
976a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      /* CF2_Fixed  hShift, */
977a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      CF2_ArrStack          hStemHintArray,
978a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      CF2_ArrStack          vStemHintArray,
979a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      CF2_HintMask          hintMask,
980a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      CF2_Fixed             hintOriginY,
981a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      const CF2_Blues       blues,
982a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      const FT_Vector*      fractionalTranslation )
983a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
984a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ZERO( glyphpath );
985a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
986a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->font      = font;
987a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->callbacks = callbacks;
988a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
989a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_arrstack_init( &glyphpath->hintMoves,
990a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       font->memory,
991a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       &font->error,
992a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                       sizeof ( CF2_HintMoveRec ) );
993a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
994a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_hintmap_init( &glyphpath->initialHintMap,
995a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      font,
996a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      &glyphpath->initialHintMap,
997a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      &glyphpath->hintMoves,
998a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      scaleY );
999a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_hintmap_init( &glyphpath->firstHintMap,
1000a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      font,
1001a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      &glyphpath->initialHintMap,
1002a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      &glyphpath->hintMoves,
1003a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      scaleY );
1004a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_hintmap_init( &glyphpath->hintMap,
1005a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      font,
1006a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      &glyphpath->initialHintMap,
1007a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      &glyphpath->hintMoves,
1008a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                      scaleY );
1009a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1010a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->scaleX = font->innerTransform.a;
1011a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->scaleC = font->innerTransform.c;
1012a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->scaleY = font->innerTransform.d;
1013a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1014a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->fractionalTranslation = *fractionalTranslation;
1015a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1016a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#if 0
1017a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->hShift = hShift;       /* for fauxing */
1018a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#endif
1019a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1020a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->hStemHintArray = hStemHintArray;
1021a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->vStemHintArray = vStemHintArray;
1022a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->hintMask       = hintMask;      /* ptr to current mask */
1023a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->hintOriginY    = hintOriginY;
1024a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->blues          = blues;
1025a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->darken         = font->darkened; /* TODO: should we make copies? */
1026a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->xOffset        = font->darkenX;
1027a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->yOffset        = font->darkenY;
1028a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->miterLimit     = 2 * FT_MAX(
1029a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                     cf2_fixedAbs( glyphpath->xOffset ),
1030a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                     cf2_fixedAbs( glyphpath->yOffset ) );
1031a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1032a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* .1 character space unit */
1033a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->snapThreshold = cf2_floatToFixed( 0.1f );
1034a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1035a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->moveIsPending = TRUE;
1036a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->pathIsOpen    = FALSE;
1037a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->elemIsQueued  = FALSE;
1038a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1039a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1040a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1041a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
1042a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_finalize( CF2_GlyphPath  glyphpath )
1043a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1044a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_arrstack_finalize( &glyphpath->hintMoves );
1045a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1046a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1047a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1048a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*
1049a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Hint point in y-direction and apply outerTransform.
1050a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Input `current' hint map (which is actually delayed by one element).
1051a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Input x,y point in Character Space.
1052a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Output x,y point in Device Space, including translation.
1053a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   */
1054a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static void
1055a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_hintPoint( CF2_GlyphPath  glyphpath,
1056a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                           CF2_HintMap    hintmap,
1057a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                           FT_Vector*     ppt,
1058a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                           CF2_Fixed      x,
1059a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                           CF2_Fixed      y )
1060a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1061a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Vector  pt;   /* hinted point in upright DS */
1062a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1063a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1064a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    pt.x = FT_MulFix( glyphpath->scaleX, x ) +
1065a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang             FT_MulFix( glyphpath->scaleC, y );
1066a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    pt.y = cf2_hintmap_map( hintmap, y );
1067a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1068a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    ppt->x = FT_MulFix( glyphpath->font->outerTransform.a, pt.x )   +
1069a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               FT_MulFix( glyphpath->font->outerTransform.c, pt.y ) +
1070a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               glyphpath->fractionalTranslation.x;
1071a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    ppt->y = FT_MulFix( glyphpath->font->outerTransform.b, pt.x )   +
1072a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               FT_MulFix( glyphpath->font->outerTransform.d, pt.y ) +
1073a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               glyphpath->fractionalTranslation.y;
1074a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1075a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1076a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1077a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*
1078a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * From two line segments, (u1,u2) and (v1,v2), compute a point of
1079a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * intersection on the corresponding lines.
1080a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Return false if no intersection is found, or if the intersection is
1081a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * too far away from the ends of the line segments, u2 and v1.
1082a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
1083a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   */
1084a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static FT_Bool
1085a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_computeIntersection( CF2_GlyphPath     glyphpath,
1086a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                     const FT_Vector*  u1,
1087a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                     const FT_Vector*  u2,
1088a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                     const FT_Vector*  v1,
1089a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                     const FT_Vector*  v2,
1090a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                     FT_Vector*        intersection )
1091a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1092a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /*
1093a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Let `u' be a zero-based vector from the first segment, `v' from the
1094a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * second segment.
1095a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Let `w 'be the zero-based vector from `u1' to `v1'.
1096a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * `perp' is the `perpendicular dot product'; see
1097a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * http://mathworld.wolfram.com/PerpDotProduct.html.
1098a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * `s' is the parameter for the parametric line for the first segment
1099a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * (`u').
1100a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *
1101a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * See notation in
1102a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * http://softsurfer.com/Archive/algorithm_0104/algorithm_0104B.htm.
1103a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Calculations are done in 16.16, but must handle the squaring of
1104a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * line lengths in character space.  We scale all vectors by 1/32 to
1105a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * avoid overflow.  This allows values up to 4095 to be squared.  The
1106a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * scale factor cancels in the divide.
1107a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *
1108a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * TODO: the scale factor could be computed from UnitsPerEm.
1109a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *
1110a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     */
1111a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1112a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#define cf2_perp( a, b )                                    \
1113a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          ( FT_MulFix( a.x, b.y ) - FT_MulFix( a.y, b.x ) )
1114a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1115a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* round and divide by 32 */
1116a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang#define CF2_CS_SCALE( x )         \
1117a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          ( ( (x) + 0x10 ) >> 5 )
1118a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1119a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Vector  u, v, w;      /* scaled vectors */
1120a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Fixed  denominator, s;
1121a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1122a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1123a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    u.x = CF2_CS_SCALE( u2->x - u1->x );
1124a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    u.y = CF2_CS_SCALE( u2->y - u1->y );
1125a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    v.x = CF2_CS_SCALE( v2->x - v1->x );
1126a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    v.y = CF2_CS_SCALE( v2->y - v1->y );
1127a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    w.x = CF2_CS_SCALE( v1->x - u1->x );
1128a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    w.y = CF2_CS_SCALE( v1->y - u1->y );
1129a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1130a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    denominator = cf2_perp( u, v );
1131a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1132a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( denominator == 0 )
1133a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      return FALSE;           /* parallel or coincident lines */
1134a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1135a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    s = FT_DivFix( cf2_perp( w, v ), denominator );
1136a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1137a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    intersection->x = u1->x + FT_MulFix( s, u2->x - u1->x );
1138a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    intersection->y = u1->y + FT_MulFix( s, u2->y - u1->y );
1139a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1140a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /*
1141a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Special case snapping for horizontal and vertical lines.
1142a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * This cleans up intersections and reduces problems with winding
1143a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * order detection.
1144a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Sample case is sbc cd KozGoPr6N-Medium.otf 20 16685.
1145a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     * Note: these calculations are in character space.
1146a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     *
1147a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang     */
1148a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1149a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( u1->x == u2->x                                                     &&
1150a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         cf2_fixedAbs( intersection->x - u1->x ) < glyphpath->snapThreshold )
1151a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      intersection->x = u1->x;
1152a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( u1->y == u2->y                                                     &&
1153a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         cf2_fixedAbs( intersection->y - u1->y ) < glyphpath->snapThreshold )
1154a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      intersection->y = u1->y;
1155a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1156a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( v1->x == v2->x                                                     &&
1157a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         cf2_fixedAbs( intersection->x - v1->x ) < glyphpath->snapThreshold )
1158a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      intersection->x = v1->x;
1159a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( v1->y == v2->y                                                     &&
1160a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         cf2_fixedAbs( intersection->y - v1->y ) < glyphpath->snapThreshold )
1161a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      intersection->y = v1->y;
1162a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1163a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* limit the intersection distance from midpoint of u2 and v1 */
1164a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( cf2_fixedAbs( intersection->x - ( u2->x + v1->x ) / 2 ) >
1165a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang           glyphpath->miterLimit                                   ||
1166a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         cf2_fixedAbs( intersection->y - ( u2->y + v1->y ) / 2 ) >
1167a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang           glyphpath->miterLimit                                   )
1168a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      return FALSE;
1169a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1170a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    return TRUE;
1171a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1172a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1173a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1174a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*
1175a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Push the cached element (glyphpath->prevElem*) to the outline
1176a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * consumer.  When a darkening offset is used, the end point of the
1177a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * cached element may be adjusted to an intersection point or it may be
1178a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * connected by a line to the current element.  This calculation must
1179a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * use a HintMap that was valid at the time the element was saved.  For
1180a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * the first point in a subpath, that is a saved HintMap.  For most
1181a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * elements, it just means the caller has delayed building a HintMap
1182a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * from the current HintMask.
1183a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
1184a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * Transform each point with outerTransform and call the outline
1185a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * callbacks.  This is a general 3x3 transform:
1186a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
1187a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *   x' = a*x + c*y + tx, y' = b*x + d*y + ty
1188a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
1189a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * but it uses 4 elements from CF2_Font and the translation part
1190a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * from CF2_GlyphPath.
1191a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
1192a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   */
1193a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static void
1194a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_pushPrevElem( CF2_GlyphPath  glyphpath,
1195a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                              CF2_HintMap    hintmap,
1196a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                              FT_Vector*     nextP0,
1197a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                              FT_Vector      nextP1,
1198a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                              FT_Bool        close )
1199a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1200a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_CallbackParamsRec  params;
1201a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1202a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Vector*  prevP0;
1203a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Vector*  prevP1;
1204a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1205a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Vector  intersection    = { 0, 0 };
1206a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Bool    useIntersection = FALSE;
1207a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1208a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1209a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_ASSERT( glyphpath->prevElemOp == CF2_PathOpLineTo ||
1210a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang               glyphpath->prevElemOp == CF2_PathOpCubeTo );
1211a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1212a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( glyphpath->prevElemOp == CF2_PathOpLineTo )
1213a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1214a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      prevP0 = &glyphpath->prevElemP0;
1215a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      prevP1 = &glyphpath->prevElemP1;
1216a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1217a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else
1218a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1219a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      prevP0 = &glyphpath->prevElemP2;
1220a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      prevP1 = &glyphpath->prevElemP3;
1221a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1222a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1223a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* optimization: if previous and next elements are offset by the same */
1224a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* amount, then there will be no gap, and no need to compute an       */
1225a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* intersection.                                                      */
1226a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( prevP1->x != nextP0->x || prevP1->y != nextP0->y )
1227a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1228a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* previous element does not join next element:             */
1229a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* adjust end point of previous element to the intersection */
1230a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      useIntersection = cf2_glyphpath_computeIntersection( glyphpath,
1231a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                                           prevP0,
1232a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                                           prevP1,
1233a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                                           nextP0,
1234a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                                           &nextP1,
1235a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                                           &intersection );
1236a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( useIntersection )
1237a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
1238a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* modify the last point of the cached element (either line or */
1239a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* curve)                                                      */
1240a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        *prevP1 = intersection;
1241a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
1242a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1243a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1244a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    params.pt0 = glyphpath->currentDS;
1245a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1246a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    switch( glyphpath->prevElemOp )
1247a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1248a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    case CF2_PathOpLineTo:
1249a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      params.op = CF2_PathOpLineTo;
1250a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1251a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* note: pt2 and pt3 are unused */
1252a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_hintPoint( glyphpath,
1253a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               hintmap,
1254a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               &params.pt1,
1255a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               glyphpath->prevElemP1.x,
1256a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               glyphpath->prevElemP1.y );
1257a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1258a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->callbacks->lineTo( glyphpath->callbacks, &params );
1259a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1260a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->currentDS = params.pt1;
1261a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1262a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      break;
1263a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1264a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    case CF2_PathOpCubeTo:
1265a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      params.op = CF2_PathOpCubeTo;
1266a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1267a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */
1268a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_hintPoint( glyphpath,
1269a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               hintmap,
1270a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               &params.pt1,
1271a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               glyphpath->prevElemP1.x,
1272a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               glyphpath->prevElemP1.y );
1273a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_hintPoint( glyphpath,
1274a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               hintmap,
1275a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               &params.pt2,
1276a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               glyphpath->prevElemP2.x,
1277a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               glyphpath->prevElemP2.y );
1278a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_hintPoint( glyphpath,
1279a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               hintmap,
1280a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               &params.pt3,
1281a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               glyphpath->prevElemP3.x,
1282a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               glyphpath->prevElemP3.y );
1283a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1284a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->callbacks->cubeTo( glyphpath->callbacks, &params );
1285a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1286a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->currentDS = params.pt3;
1287a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1288a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      break;
1289a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1290a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1291a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( !useIntersection || close )
1292a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1293a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* insert connecting line between end of previous element and start */
1294a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* of current one                                                   */
1295a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* note: at the end of a subpath, we might do both, so use `nextP0' */
1296a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* before we change it, below                                       */
1297a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1298a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_hintPoint( glyphpath,
1299a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               hintmap,
1300a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               &params.pt1,
1301a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               nextP0->x,
1302a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               nextP0->y );
1303a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1304a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( params.pt1.x != glyphpath->currentDS.x ||
1305a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang           params.pt1.y != glyphpath->currentDS.y )
1306a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
1307a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* length is nonzero */
1308a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        params.op  = CF2_PathOpLineTo;
1309a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        params.pt0 = glyphpath->currentDS;
1310a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1311a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* note: pt2 and pt3 are unused */
1312a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        glyphpath->callbacks->lineTo( glyphpath->callbacks, &params );
1313a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1314a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        glyphpath->currentDS = params.pt1;
1315a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
1316a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1317a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1318a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( useIntersection )
1319a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1320a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* return intersection point to caller */
1321a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      *nextP0 = intersection;
1322a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1323a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1324a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1325a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1326a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* push a MoveTo element based on current point and offset of current */
1327a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /* element                                                            */
1328a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static void
1329a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_pushMove( CF2_GlyphPath  glyphpath,
1330a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          FT_Vector      start )
1331a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1332a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_CallbackParamsRec  params;
1333a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1334a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1335a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    params.op  = CF2_PathOpMoveTo;
1336a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    params.pt0 = glyphpath->currentDS;
1337a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1338a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* Test if move has really happened yet; it would have called */
1339a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* `cf2_hintmap_build' to set `isValid'.                   */
1340a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) )
1341a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1342a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* we are here iff first subpath is missing a moveto operator: */
1343a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* synthesize first moveTo to finish initialization of hintMap */
1344a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_moveTo( glyphpath,
1345a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                            glyphpath->start.x,
1346a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                            glyphpath->start.y );
1347a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1348a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1349a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_glyphpath_hintPoint( glyphpath,
1350a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                             &glyphpath->hintMap,
1351a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                             &params.pt1,
1352a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                             start.x,
1353a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                             start.y );
1354a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1355a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* note: pt2 and pt3 are unused */
1356a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->callbacks->moveTo( glyphpath->callbacks, &params );
1357a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1358a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->currentDS    = params.pt1;
1359a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->offsetStart0 = start;
1360a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1361a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1362a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1363a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  /*
1364a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * All coordinates are in character space.
1365a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * On input, (x1, y1) and (x2, y2) give line segment.
1366a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * On output, (x, y) give offset vector.
1367a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * We use a piecewise approximation to trig functions.
1368a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *
1369a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   * TODO: Offset true perpendicular and proper length
1370a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *       supply the y-translation for hinting here, too,
1371a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   *       that adds yOffset unconditionally to *y.
1372a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang   */
1373a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  static void
1374a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_computeOffset( CF2_GlyphPath  glyphpath,
1375a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               CF2_Fixed      x1,
1376a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               CF2_Fixed      y1,
1377a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               CF2_Fixed      x2,
1378a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               CF2_Fixed      y2,
1379a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               CF2_Fixed*     x,
1380a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                               CF2_Fixed*     y )
1381a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1382a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Fixed  dx = x2 - x1;
1383a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Fixed  dy = y2 - y1;
1384a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1385a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1386a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* note: negative offsets don't work here; negate deltas to change */
1387a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* quadrants, below                                                */
1388a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( glyphpath->font->reverseWinding )
1389a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1390a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      dx = -dx;
1391a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      dy = -dy;
1392a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1393a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1394a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    *x = *y = 0;
1395a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1396a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( !glyphpath->darken )
1397a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        return;
1398a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1399a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* add momentum for this path element */
1400a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->callbacks->windingMomentum +=
1401a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_getWindingMomentum( x1, y1, x2, y2 );
1402a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1403a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* note: allow mixed integer and fixed multiplication here */
1404a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( dx >= 0 )
1405a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1406a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( dy >= 0 )
1407a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
1408a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* first quadrant, +x +y */
1409a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1410a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( dx > 2 * dy )
1411a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1412a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* +x */
1413a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = 0;
1414a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = 0;
1415a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1416a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else if ( dy > 2 * dx )
1417a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1418a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* +y */
1419a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = glyphpath->xOffset;
1420a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = glyphpath->yOffset;
1421a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1422a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else
1423a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1424a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* +x +y */
1425a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = FT_MulFix( cf2_floatToFixed( 0.7 ),
1426a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          glyphpath->xOffset );
1427a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = FT_MulFix( cf2_floatToFixed( 1.0 - 0.7 ),
1428a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          glyphpath->yOffset );
1429a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1430a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
1431a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
1432a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
1433a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* fourth quadrant, +x -y */
1434a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1435a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( dx > -2 * dy )
1436a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1437a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* +x */
1438a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = 0;
1439a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = 0;
1440a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1441a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else if ( -dy > 2 * dx )
1442a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1443a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* -y */
1444a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = -glyphpath->xOffset;
1445a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = glyphpath->yOffset;
1446a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1447a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else
1448a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1449a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* +x -y */
1450a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = FT_MulFix( cf2_floatToFixed( -0.7 ),
1451a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          glyphpath->xOffset );
1452a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = FT_MulFix( cf2_floatToFixed( 1.0 - 0.7 ),
1453a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          glyphpath->yOffset );
1454a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1455a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
1456a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1457a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    else
1458a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1459a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      if ( dy >= 0 )
1460a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
1461a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* second quadrant, -x +y */
1462a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1463a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( -dx > 2 * dy )
1464a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1465a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* -x */
1466a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = 0;
1467a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = 2 * glyphpath->yOffset;
1468a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1469a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else if ( dy > -2 * dx )
1470a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1471a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* +y */
1472a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = glyphpath->xOffset;
1473a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = glyphpath->yOffset;
1474a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1475a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else
1476a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1477a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* -x +y */
1478a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = FT_MulFix( cf2_floatToFixed( 0.7 ),
1479a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          glyphpath->xOffset );
1480a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ),
1481a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          glyphpath->yOffset );
1482a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1483a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
1484a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      else
1485a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      {
1486a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        /* third quadrant, -x -y */
1487a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1488a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        if ( -dx > -2 * dy )
1489a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1490a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* -x */
1491a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = 0;
1492a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = 2 * glyphpath->yOffset;
1493a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1494a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else if ( -dy > -2 * dx )
1495a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1496a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* -y */
1497a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = -glyphpath->xOffset;
1498a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = glyphpath->xOffset;
1499a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1500a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        else
1501a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        {
1502a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          /* -x -y */
1503a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *x = FT_MulFix( cf2_floatToFixed( -0.7 ),
1504a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          glyphpath->xOffset );
1505a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang          *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ),
1506a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                          glyphpath->yOffset );
1507a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang        }
1508a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      }
1509a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1510a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1511a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1512a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1513a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
1514a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_moveTo( CF2_GlyphPath  glyphpath,
1515a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                        CF2_Fixed      x,
1516a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                        CF2_Fixed      y )
1517a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1518a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_glyphpath_closeOpenPath( glyphpath );
1519a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1520a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* save the parameters of the move for later, when we'll know how to */
1521a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* offset it;                                                        */
1522a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* also save last move point */
1523a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->currentCS.x = glyphpath->start.x = x;
1524a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->currentCS.y = glyphpath->start.y = y;
1525a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1526a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->moveIsPending = TRUE;
1527a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1528a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* ensure we have a valid map with current mask */
1529a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) ||
1530a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang         cf2_hintmask_isNew( glyphpath->hintMask )   )
1531a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hintmap_build( &glyphpath->hintMap,
1532a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hStemHintArray,
1533a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->vStemHintArray,
1534a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hintMask,
1535a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hintOriginY,
1536a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         FALSE );
1537a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1538a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* save a copy of current HintMap to use when drawing initial point */
1539a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->firstHintMap = glyphpath->hintMap;     /* structure copy */
1540a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1541a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1542a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1543a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
1544a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_lineTo( CF2_GlyphPath  glyphpath,
1545a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                        CF2_Fixed      x,
1546a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                        CF2_Fixed      y )
1547a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1548a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Fixed  xOffset, yOffset;
1549a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Vector  P0, P1;
1550a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1551a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1552a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* can't compute offset of zero length line, so ignore them */
1553a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( glyphpath->currentCS.x == x && glyphpath->currentCS.y == y )
1554a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      return;
1555a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1556a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_glyphpath_computeOffset( glyphpath,
1557a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 glyphpath->currentCS.x,
1558a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 glyphpath->currentCS.y,
1559a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 x,
1560a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 y,
1561a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 &xOffset,
1562a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 &yOffset );
1563a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1564a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* construct offset points */
1565a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P0.x = glyphpath->currentCS.x + xOffset;
1566a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P0.y = glyphpath->currentCS.y + yOffset;
1567a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P1.x = x + xOffset;
1568a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P1.y = y + yOffset;
1569a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1570a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( glyphpath->moveIsPending )
1571a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1572a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* emit offset 1st point as MoveTo */
1573a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_pushMove( glyphpath, P0 );
1574a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1575a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->moveIsPending = FALSE;  /* adjust state machine */
1576a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->pathIsOpen    = TRUE;
1577a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1578a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->offsetStart1 = P1;              /* record second point */
1579a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1580a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1581a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( glyphpath->elemIsQueued )
1582a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1583a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) );
1584a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1585a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_pushPrevElem( glyphpath,
1586a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  &glyphpath->hintMap,
1587a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  &P0,
1588a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  P1,
1589a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  FALSE );
1590a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1591a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1592a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* queue the current element with offset points */
1593a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->elemIsQueued = TRUE;
1594a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->prevElemOp   = CF2_PathOpLineTo;
1595a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->prevElemP0   = P0;
1596a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->prevElemP1   = P1;
1597a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1598a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* update current map */
1599a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( cf2_hintmask_isNew( glyphpath->hintMask ) )
1600a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hintmap_build( &glyphpath->hintMap,
1601a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hStemHintArray,
1602a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->vStemHintArray,
1603a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hintMask,
1604a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hintOriginY,
1605a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         FALSE );
1606a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1607a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->currentCS.x = x;     /* pre-offset current point */
1608a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->currentCS.y = y;
1609a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1610a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1611a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1612a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
1613a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_curveTo( CF2_GlyphPath  glyphpath,
1614a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         CF2_Fixed      x1,
1615a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         CF2_Fixed      y1,
1616a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         CF2_Fixed      x2,
1617a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         CF2_Fixed      y2,
1618a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         CF2_Fixed      x3,
1619a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         CF2_Fixed      y3 )
1620a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1621a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    CF2_Fixed  xOffset1, yOffset1, xOffset3, yOffset3;
1622a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    FT_Vector  P0, P1, P2, P3;
1623a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1624a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1625a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* TODO: ignore zero length portions of curve?? */
1626a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_glyphpath_computeOffset( glyphpath,
1627a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 glyphpath->currentCS.x,
1628a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 glyphpath->currentCS.y,
1629a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 x1,
1630a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 y1,
1631a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 &xOffset1,
1632a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 &yOffset1 );
1633a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    cf2_glyphpath_computeOffset( glyphpath,
1634a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 x2,
1635a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 y2,
1636a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 x3,
1637a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 y3,
1638a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 &xOffset3,
1639a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                 &yOffset3 );
1640a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1641a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* add momentum from the middle segment */
1642a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->callbacks->windingMomentum +=
1643a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_getWindingMomentum( x1, y1, x2, y2 );
1644a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1645a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* construct offset points */
1646a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P0.x = glyphpath->currentCS.x + xOffset1;
1647a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P0.y = glyphpath->currentCS.y + yOffset1;
1648a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P1.x = x1 + xOffset1;
1649a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P1.y = y1 + yOffset1;
1650a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* note: preserve angle of final segment by using offset3 at both ends */
1651a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P2.x = x2 + xOffset3;
1652a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P2.y = y2 + yOffset3;
1653a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P3.x = x3 + xOffset3;
1654a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    P3.y = y3 + yOffset3;
1655a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1656a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( glyphpath->moveIsPending )
1657a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1658a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* emit offset 1st point as MoveTo */
1659a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_pushMove( glyphpath, P0 );
1660a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1661a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->moveIsPending = FALSE;
1662a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->pathIsOpen    = TRUE;
1663a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1664a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->offsetStart1 = P1;              /* record second point */
1665a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1666a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1667a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( glyphpath->elemIsQueued )
1668a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1669a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) );
1670a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1671a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_pushPrevElem( glyphpath,
1672a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  &glyphpath->hintMap,
1673a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  &P0,
1674a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  P1,
1675a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  FALSE );
1676a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1677a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1678a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* queue the current element with offset points */
1679a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->elemIsQueued = TRUE;
1680a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->prevElemOp   = CF2_PathOpCubeTo;
1681a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->prevElemP0   = P0;
1682a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->prevElemP1   = P1;
1683a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->prevElemP2   = P2;
1684a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->prevElemP3   = P3;
1685a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1686a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    /* update current map */
1687a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( cf2_hintmask_isNew( glyphpath->hintMask ) )
1688a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_hintmap_build( &glyphpath->hintMap,
1689a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hStemHintArray,
1690a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->vStemHintArray,
1691a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hintMask,
1692a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         glyphpath->hintOriginY,
1693a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                         FALSE );
1694a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1695a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->currentCS.x = x3;       /* pre-offset current point */
1696a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    glyphpath->currentCS.y = y3;
1697a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1698a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1699a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1700a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  FT_LOCAL_DEF( void )
1701a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  cf2_glyphpath_closeOpenPath( CF2_GlyphPath  glyphpath )
1702a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  {
1703a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    if ( glyphpath->pathIsOpen )
1704a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    {
1705a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( cf2_hintmap_isValid( &glyphpath->firstHintMap ) );
1706a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1707a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* since we need to apply an offset to the implicit lineto, we make */
1708a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* it explicit here                                                 */
1709a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_lineTo( glyphpath,
1710a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                            glyphpath->start.x,
1711a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                            glyphpath->start.y );
1712a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1713a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* Draw previous element (the explicit LineTo we just created,      */
1714a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* above) and connect it to the start point, but with the offset we */
1715a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* saved from the first element.                                    */
1716a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* Use the saved HintMap, too. */
1717a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      FT_ASSERT( glyphpath->elemIsQueued );
1718a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1719a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      cf2_glyphpath_pushPrevElem( glyphpath,
1720a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  &glyphpath->firstHintMap,
1721a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  &glyphpath->offsetStart0,
1722a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  glyphpath->offsetStart1,
1723a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang                                  TRUE );
1724a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1725a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      /* reset state machine */
1726a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->moveIsPending = TRUE;
1727a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->pathIsOpen    = FALSE;
1728a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang      glyphpath->elemIsQueued  = FALSE;
1729a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang    }
1730a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang  }
1731a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1732a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang
1733a2b9955b49034a51dfbc8bf9f4e9d312149cecacXianzhu Wang/* END */
1734