w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

nowakowskittfinstr.c
Go to the documentation of this file.
1 /* Copyright (C) 2000-2012 by
2  George Williams, Michal Nowakowski & Alexey Kryukov */
3 
4 /*
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7 
8  * Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10 
11  * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14 
15  * The name of the author may not be used to endorse or promote products
16  * derived from this software without specific prior written permission.
17 
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
19  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include "fontforgevw.h"
30 #define _USE_MATH_DEFINES
31 #include <ctype.h>
32 #include <math.h>
33 
34 #include "ttf.h"
35 #include "splinefont.h"
36 #include "stemdb.h"
37 
38 extern int autohint_before_generate;
39 
44  interpolate_more_strong = 1, /* not applicable if interpolate_strong==0 */
46 
47 /* non-optimized instructions will be using a stack of depth 6, allowing
48  * for easy testing whether the code leaves trash on the stack or not.
49  */
50 #define OPTIMIZE_TTF_INSTRS 1
51 #if OPTIMIZE_TTF_INSTRS
52 #define STACK_DEPTH 256
53 #else
54 #define STACK_DEPTH 6
55 #endif
56 
57 /* define some often used instructions */
58 #define SVTCA_y (0x00)
59 #define SVTCA_x (0x01)
60 #define SRP0 (0x10)
61 #define SRP1 (0x11)
62 #define SRP2 (0x12)
63 #define SZP0 (0x13)
64 #define SLOOP (0x17)
65 #define RTG (0x18)
66 #define SMD (0x1a)
67 #define DUP (0x20)
68 #define DEPTH (0x24)
69 #define CALL (0x2b)
70 #define MDAP (0x2e)
71 #define MDAP_rnd (0x2f)
72 #define IUP_y (0x30)
73 #define IUP_x (0x31)
74 #define SHP_rp2 (0x32)
75 #define SHP_rp1 (0x33)
76 #define SHPIX (0x38)
77 #define IP (0x39)
78 #define ALIGNRP (0x3c)
79 #define MIAP_rnd (0x3f)
80 #define ADD (0x60)
81 #define MUL (0x63)
82 #define NEG (0x65)
83 #define SROUND (0x76)
84 #define FLIPPT (0x80)
85 #define MDRP_grey (0xc0)
86 #define MDRP_min_black (0xc9)
87 #define MDRP_min_white (0xca)
88 #define MDRP_min_rnd_black (0xcd)
89 #define MDRP_rp0_rnd_white (0xd6)
90 #define MDRP_rp0_min_rnd_grey (0xdc)
91 #define MDRP_rp0_min_rnd_black (0xdd)
92 #define MIRP_min_black (0xe9)
93 #define MIRP_min_rnd_black (0xed)
94 #define MIRP_rp0_min_black (0xf9)
95 #define MIRP_rp0_min_rnd_black (0xfd)
96 
97 
98 /******************************************************************************
99  *
100  * Low-level routines to add data for PUSHes to bytecode instruction stream.
101  * pushheader() adds PUSH preamble, then repeating addpoint() adds items.
102  *
103  * Numbers larger than 65535 are not supported (according to TrueType spec,
104  * there can't be more points in a glyph, simple or compound). Negative
105  * numbers aren't supported, either. So don't use these functions as they
106  * are - there are higher-level ones further below, that handle things nicely.
107  *
108  ******************************************************************************/
109 
110 static uint8 *pushheader(uint8 *instrs, int isword, int tot) {
111  if ( isword ) {
112  if ( tot>8 ) {
113  *instrs++ = 0x41; /* N(next word) Push words */
114  *instrs++ = tot;
115  } else
116  *instrs++ = 0xb8+(tot-1); /* Push Words */
117  } else {
118  if ( tot>8 ) {
119  *instrs++ = 0x40; /* N(next byte) Push bytes */
120  *instrs++ = tot;
121  } else
122  *instrs++ = 0xb0+(tot-1); /* Push bytes */
123  }
124 return( instrs );
125 }
126 
127 static uint8 *addpoint(uint8 *instrs,int isword,int pt) {
128  if ( !isword ) {
129  *instrs++ = pt;
130  } else {
131  *instrs++ = pt>>8;
132  *instrs++ = pt&0xff;
133  }
134 return( instrs );
135 }
136 
137 /* Exemplary high-level routines to add PUSH-es to bytecode instruction
138  * stream. They handle negative numbers correctly. As they are used
139  * in various roles here, some aliases are defined, so that the name
140  * speaks for itself in the code.
141  */
142 
143 static uint8 *pushpoint(uint8 *instrs,int pt) {
144  instrs = pushheader(instrs,(pt>255)||(pt<0),1);
145 return( addpoint(instrs,(pt>255)||(pt<0),pt));
146 }
147 
148 #define pushnum(a, b) pushpoint(a, b)
149 
150 static uint8 *pushpointstem(uint8 *instrs, int pt, int stem) {
151  int isword = pt>255 || stem>255 || pt<0 || stem<0;
152  instrs = pushheader(instrs,isword,2);
153  instrs = addpoint(instrs,isword,pt);
154 return( addpoint(instrs,isword,stem));
155 }
156 
157 #define push2points(a, b, c) pushpointstem(a, b, c)
158 #define push2nums(a, b, c) pushpointstem(a, b, c)
159 
160 /* Push a bunch of point numbers (or other numbers) onto the stack.
161  * TODO!
162  * Possible strategies:
163  * - push point by point (poor space efficiency)
164  * - push all the stock at once (currently used, better, but has
165  * poor space efficiency in case of a word among several bytes).
166  * - push bytes and words separately
167  */
168 static uint8 *pushpoints(uint8 *instrs, int ptcnt, const int *pts) {
169  int i, isword = 0;
170  for (i=0; i<ptcnt; i++) if (pts[i]>255 || pts[i]<0) isword=1;
171 
172  /* It's an error to push more than STACK_DEPTH points. */
173  if (ptcnt > STACK_DEPTH)
174  IError("Truetype stack overflow will occur.");
175 
176  if (ptcnt > 255 && !isword) {
177  instrs = pushpoints(instrs, 255, pts);
178  ptcnt-=255;
179  pts+=255;
180  }
181 
182  instrs = pushheader(instrs,isword,ptcnt);
183  for (i=0; i<ptcnt; i++) instrs = addpoint(instrs, isword, pts[i]);
184 return( instrs );
185 }
186 
187 #define pushnums(a, b, c) pushpoints(a, b, c)
188 
189 /* As we don't have "push F26dot6" command in truetype instructions,
190  * we need to do this by hand. As we can explicitly push only 16-bit
191  * quantities, we need to push a F26dot6 value in halves, shift left
192  * the more significant half and add halves.
193  *
194  * There are no checks for overflow!
195  */
196 static uint8 *pushF26Dot6(uint8 *instrs, double num) {
197  int a, elems[3];
198  int negative=0;
199 
200  if (num < 0) {
201  negative=1;
202  num*=-1.0;
203  }
204 
205  num *= 64;
206  a = rint(num);
207  elems[0] = a % 65536;
208  elems[1] = (int)rint(a / 65536.0) % 65536;
209  elems[2] = 16384;
210 
211  if (elems[1]) {
212  instrs = pushpoints(instrs, 3, elems);
213  *instrs++ = DUP;
214  *instrs++ = MUL;
215  *instrs++ = MUL;
216  *instrs++ = ADD;
217  }
218  else instrs = pushpoint(instrs, elems[0]);
219 
220  if (negative) *instrs++ = NEG;
221 
222 return( instrs );
223 }
224 
225 /* Compute an EF2Dot14 representation of a floating point number.
226  * The number must be in range [-2.0 ... 1.0+(2^14-1)/(2^14) = 1.99993896...]
227  *
228  * There are no checks for overflow!
229  */
230 static int EF2Dot14(double num) {
231 return( rint(num*16384) );
232 }
233 
234 /* An apparatus for instructing sets of points with given truetype command.
235  * The command must pop exactly 1 element from the stack and mustn't push any.
236  * These points must be marked as 'touched' elsewhere! this function only
237  * generates intructions.
238  */
239 static uint8 *instructpoints(uint8 *instrs, int ptcnt, const int *pts, uint8 command) {
240  int i, use_sloop;
241 
242  use_sloop = 0;
243  use_sloop |= (command == SHP_rp1);
244  use_sloop |= (command == SHP_rp2);
245  use_sloop |= (command == SHPIX);
246  use_sloop |= (command == IP);
247  use_sloop |= (command == FLIPPT);
248  use_sloop |= (command == ALIGNRP);
249  use_sloop = use_sloop && (ptcnt > 3);
250 
251  instrs = pushpoints(instrs, ptcnt<STACK_DEPTH?ptcnt:STACK_DEPTH-1, pts);
252 
253  if (use_sloop) {
254  *instrs++ = DEPTH;
255  *instrs++ = SLOOP;
256  *instrs++ = command;
257  }
258  else for (i=0; i<(ptcnt<STACK_DEPTH?ptcnt:STACK_DEPTH-1); i++)
259  *instrs++ = command;
260 
261  if (ptcnt>=STACK_DEPTH)
262  instrs=instructpoints(instrs, ptcnt-(STACK_DEPTH-1), pts+(STACK_DEPTH-1), command);
263 
264 return( instrs );
265 }
266 
267 /******************************************************************************
268  *
269  * Low-level routines for getting a cvt index for a stem width, assuming there
270  * are any numbers in cvt. Includes legacy code for importing PS Private into
271  * CVT.
272  *
273  ******************************************************************************/
274 
276  struct ttf_table *tab;
277 
278  for ( tab=sf->ttf_tables; tab!=NULL && tab->tag!=tag; tab=tab->next );
279 return( tab );
280 }
281 
283  int i;
284  struct ttf_table *cvt_tab = SFFindTable(sf,CHR('c','v','t',' '));
285 
286  if ( cvt_tab==NULL ) {
287  cvt_tab = chunkalloc(sizeof(struct ttf_table));
288  cvt_tab->tag = CHR('c','v','t',' ');
289  cvt_tab->maxlen = 200;
290  cvt_tab->data = malloc(100*sizeof(short));
291  cvt_tab->next = sf->ttf_tables;
292  sf->ttf_tables = cvt_tab;
293  }
294  for ( i=0; (int)sizeof(uint16)*i<cvt_tab->len; ++i ) {
295  int tval = (int16) memushort(cvt_tab->data,cvt_tab->len, sizeof(uint16)*i);
296  if ( val>=tval-1 && val<=tval+1 )
297 return( i );
298  }
299  if ( (int)sizeof(uint16)*i>=cvt_tab->maxlen ) {
300  if ( cvt_tab->maxlen==0 ) cvt_tab->maxlen = cvt_tab->len;
301  cvt_tab->maxlen += 200;
302  cvt_tab->data = realloc(cvt_tab->data,cvt_tab->maxlen);
303  }
304  memputshort(cvt_tab->data,sizeof(uint16)*i,val);
305  cvt_tab->len += sizeof(uint16);
306 return( i );
307 }
308 
309 /* by default sign is unimportant in the cvt
310  * For some instructions anyway, but not for MIAP so this routine has
311  * been broken in two.
312  */
314  if ( val<0 ) val = -val;
315 return( TTF__getcvtval(sf,val));
316 }
317 
318 /* We are given a stem weight and try to find matching one in CVT.
319  * If none found, we return -1.
320  */
321 static StdStem *CVTSeekStem(int xdir, GlobalInstrCt *gic, double value, int can_fail) {
322  StdStem *mainstem = xdir?&(gic->stdvw):&(gic->stdhw);
323  StdStem *otherstems = xdir?gic->stemsnapv:gic->stemsnaph;
324  StdStem *closest = NULL;
325  int otherstemcnt = xdir?gic->stemsnapvcnt:gic->stemsnaphcnt;
326  int i;
327  double mindelta=1e20, delta, closestwidth=1e20;
328 
329  if (mainstem->width == -1)
330 return NULL;
331 
332  value = fabs(value);
333  delta = fabs(mainstem->width - value);
334 
335  if (delta < mindelta) {
336  mindelta = delta;
337  closestwidth = rint(mainstem->width);
338  closest = mainstem;
339  }
340 
341  for (i=0; i<otherstemcnt; i++) {
342  delta = fabs(otherstems[i].width - value);
343 
344  if (delta < mindelta) {
345  mindelta = delta;
346  closestwidth = otherstems[i].width;
347  closest = otherstems+i;
348  }
349  }
350 
351  if (mindelta <= gic->fudge)
352 return closest;
353  if (value/closestwidth < 1.11 && value/closestwidth > 0.9)
354 return closest;
355  if (can_fail)
356 return NULL;
357 return closest;
358 }
359 
360 /******************************************************************************
361  ******************************************************************************
362  **
363  ** We need to initialize global instructing context before autoinstructing
364  ** a glyph, because we want to be sure that global hinting tables (cvt, prep,
365  ** fpgm) were (or weren't) properly set up.
366  **
367  ******************************************************************************
368  ******************************************************************************/
369 
370 /* Helper routines: read PS private entry and return its contents.
371  */
372 static int GetBlueFuzz(SplineFont *sf) {
373  char *str, *end;
374 
375  if ( sf->private==NULL || (str=PSDictHasEntry(sf->private,"BlueFuzz"))==NULL || !isdigit(str[0]) )
376 return 1;
377 return strtod(str, &end);
378 }
379 
380 /* Return BlueScale as PPEM at which we have to stop suppressing overshoots */
381 static int GetBlueScale(SplineFont *sf) {
382  char *str, *end;
383  double bs;
384  int result;
385  if ( sf->private==NULL || (str=PSDictHasEntry(sf->private,"BlueScale"))==NULL )
386 return 42;
387 
388  bs = strtod(str, &end);
389  if (end==str || bs<=0.0) bs=0.039625;
390  bs*=240;
391  bs+=0.49;
392  bs*=300.0/72.0;
393 
394  result = (int)rint(bs);
395  if (result>255) result = 255; /* Who would need such blue scale??? */
396 
397 return result;
398 }
399 
400 static real *ParsePSArray(const char *str, int *rescnt) {
401  char *end;
402  real d, *results=NULL;
403 
404  if ((rescnt == NULL) || (str == NULL))
405 return NULL;
406 
407  *rescnt = 0;
408 
409  while (*str)
410  {
411  while (!isdigit(*str) && *str!='-' && *str!='+' && *str!='.' && *str!='\0')
412  ++str;
413 
414  if ( *str=='\0' )
415  break;
416 
417  d = strtod(str, &end);
418 
419  if ( d>=-32768 && d<=32767 ) {
420  if (*rescnt) {
421  results = realloc(results, sizeof(real)*(++(*rescnt)));
422  results[*rescnt-1] = d;
423  }
424  else (results = calloc(*rescnt=1, sizeof(real)))[0] = d;
425  }
426 
427  str = end;
428  }
429 
430 return results;
431 }
432 
433 static real *GetNParsePSArray(SplineFont *sf, const char *name, int *rescnt) {
434 return ParsePSArray(PSDictHasEntry(sf->private, name), rescnt);
435 }
436 
437 /* Tell if the two segments, [b1,o1] and [b2,o2] intersect.
438  * This can be used to determine whether blues or stems overlap.
439  */
440 static int SegmentsOverlap(real b1, real o1, real b2, real o2) {
441  real t;
442 
443  if (b1 > o1) {
444  t = o1;
445  o1 = b1;
446  b1 = t;
447  }
448 
449  if (b2 > o2) {
450  t = o2;
451  o2 = b2;
452  b2 = t;
453  }
454 
455 return !((b2 > o1) || (o2 < b1));
456 }
457 
458 /* To be used with qsort() - sorts BlueZone array by base in ascending order.
459  */
460 static int SortBlues(const void *a, const void *b) {
461  return ((BlueZone *)a)->base > ((BlueZone *)b)->base;
462 }
463 
464 /* Import blue data into global instructing context. Include family blues too.
465  * We assume that blues are needed for family blues to make sense. If there are
466  * only family blues, we treat them as normal blues. Otherwise, if a family blue
467  * zone doesn't match any normal blue zone, or if they match perfectly,
468  * it is ignored.
469  */
470 static void GICImportBlues(GlobalInstrCt *gic) {
471  int bluecnt = 0;
472  int i, j, cnt;
473  real *values;
474 
475  int HasPSBlues =
476  (PSDictHasEntry(gic->sf->private, "BlueValues") != NULL) ||
477  (PSDictHasEntry(gic->sf->private, "OtherBlues") != NULL);
478 
479  int HasPSFamilyBlues =
480  (PSDictHasEntry(gic->sf->private, "FamilyBlues") != NULL) ||
481  (PSDictHasEntry(gic->sf->private, "FamilyOtherBlues") != NULL);
482 
483  const char *PrimaryBlues = HasPSBlues ? "BlueValues" : "FamilyBlues";
484  const char *OtherBlues = HasPSBlues ? "OtherBlues" : "FamilyOtherBlues";
485 
486  if (HasPSBlues || HasPSFamilyBlues){
487  values = GetNParsePSArray(gic->sf, PrimaryBlues, &cnt);
488  cnt /= 2;
489  if (cnt > 7) cnt = 7;
490 
491  if (values != NULL) {
492  gic->bluecnt = bluecnt = cnt;
493 
494  /* First pair is a bottom zone (see Type1 specification). */
495  gic->blues[0].base = values[1];
496  gic->blues[0].overshoot = values[0];
497  gic->blues[0].family_base = strtod("NAN", NULL);
498 
499  /* Next pairs are top zones (see Type1 specification). */
500  for (i=1; i<bluecnt; i++) {
501  gic->blues[i].family_base = strtod("NAN", NULL);
502  gic->blues[i].base = values[2*i];
503  gic->blues[i].overshoot = values[2*i+1];
504  }
505 
506  free(values);
507  }
508 
509  values = GetNParsePSArray(gic->sf, OtherBlues, &cnt);
510  cnt /= 2;
511  if (cnt > 5) cnt = 5;
512 
513  if (values != NULL) {
514  gic->bluecnt += cnt;
515 
516  /* All pairs are bottom zones (see Type1 specification). */
517  for (i=0; i<cnt; i++) {
518  gic->blues[i+bluecnt].family_base = strtod("NAN", NULL);
519  gic->blues[i+bluecnt].base = values[2*i+1];
520  gic->blues[i+bluecnt].overshoot = values[2*i];
521  }
522 
523  free(values);
524  bluecnt += cnt;
525  }
526 
527  /* Add family data to blues */
528  if (HasPSBlues && HasPSFamilyBlues) {
529  values = GetNParsePSArray(gic->sf, "FamilyBlues", &cnt);
530  cnt /= 2;
531  if (cnt > 7) cnt = 7;
532 
533  if (values != NULL) {
534  /* First pair is a bottom zone (see Type1 specification). */
535  for (j=0; j<bluecnt; j++)
536  if (isfinite(gic->blues[j].family_base))
537  continue;
538  else if (values[1] != gic->blues[j].base &&
539  SegmentsOverlap(gic->blues[j].base,
540  gic->blues[j].overshoot,
541  values[0], values[1]))
542  gic->blues[j].family_base = values[1];
543 
544  /* Next pairs are top zones (see Type1 specification). */
545  for (i=1; i<cnt; i++) {
546  for (j=0; j<bluecnt; j++)
547  if (isfinite(gic->blues[j].family_base))
548  continue;
549  else if (values[2*i] != gic->blues[j].base &&
550  SegmentsOverlap(gic->blues[j].base,
551  gic->blues[j].overshoot,
552  values[2*i], values[2*i+1]))
553  gic->blues[j].family_base = values[2*i];
554  }
555 
556  free(values);
557  }
558 
559  values = GetNParsePSArray(gic->sf, "FamilyOtherBlues", &cnt);
560  cnt /= 2;
561  if (cnt > 5) cnt = 5;
562 
563  if (values != NULL) {
564  /* All pairs are bottom zones (see Type1 specification). */
565  for (i=0; i<cnt; i++) {
566  for (j=0; j<bluecnt; j++)
567  if (isfinite(gic->blues[j].family_base))
568  continue;
569  else if (values[2*i+1] != gic->blues[j].base &&
570  SegmentsOverlap(gic->blues[j].base,
571  gic->blues[j].overshoot,
572  values[2*i], values[2*i+1]))
573  gic->blues[j].family_base = values[2*i+1];
574  }
575 
576  free(values);
577  }
578  }
579  }
580  else if (gic->bd->bluecnt) {
581  /* If there are no PS private entries, we have */
582  /* to use FF's quickly guessed fallback blues. */
583  gic->bluecnt = bluecnt = gic->bd->bluecnt;
584 
585  for (i=0; i<bluecnt; i++) {
586  gic->blues[i].family_base = strtod("NAN", NULL);
587  gic->blues[i].family_cvtindex = -1;
588 
589  if (gic->bd->blues[i][1] <= 0) {
590  gic->blues[i].base = gic->bd->blues[i][1];
591  gic->blues[i].overshoot = gic->bd->blues[i][0];
592  }
593  else {
594  gic->blues[i].base = gic->bd->blues[i][0];
595  gic->blues[i].overshoot = gic->bd->blues[i][1];
596  }
597  }
598  }
599 
600  /* 'highest' and 'lowest' are not to be set yet. */
601  for (i=0; i<gic->bluecnt; i++)
602  gic->blues[i].highest = gic->blues[i].lowest = -1;
603 
604  /* I assume ascending order in snap_to_blues(). */
605  qsort(gic->blues, gic->bluecnt, sizeof(BlueZone), SortBlues);
606 }
607 
608 /* To be used with qsort() - sorts StdStem array by width in ascending order.
609  */
610 static int SortStems(const void *a, const void *b) {
611  return ((StdStem *)a)->width > ((StdStem *)b)->width;
612 }
613 
614 /* Import stem data into global instructing context. We deal only with
615  * horizontal or vertical stems (xdir decides) here. If Std*W is not specified,
616  * but there exists StemSnap*, we'll make up a fake Std*V as a fallback.
617  * Subtle manipulations with Std*W's value can result in massive change of
618  * font appearance at some pixel sizes, because it's used as a base for
619  * normalization of all other stems.
620  */
621 static void GICImportStems(int xdir, GlobalInstrCt *gic) {
622  int i, cnt, next;
623  real *values;
624  const char *s_StdW = xdir?"StdVW":"StdHW";
625  const char *s_StemSnap = xdir?"StemSnapV":"StemSnapH";
626  StdStem *stdw = xdir?&(gic->stdvw):&(gic->stdhw);
627  StdStem **stemsnap = xdir?&(gic->stemsnapv):&(gic->stemsnaph);
628  int *stemsnapcnt = xdir?&(gic->stemsnapvcnt):&(gic->stemsnaphcnt);
629 
630  if ((values = GetNParsePSArray(gic->sf, s_StdW, &cnt)) != NULL) {
631  stdw->width = *values;
632  free(values);
633  }
634 
635  if ((values = GetNParsePSArray(gic->sf, s_StemSnap, &cnt)) != NULL) {
636  *stemsnap = (StdStem *)calloc(cnt, sizeof(StdStem));
637 
638  for (next=i=0; i<cnt; i++)
639  if (values[i] != gic->stdhw.width)
640  (*stemsnap)[next++].width = values[i];
641 
642  if (!next) {
643  free(*stemsnap);
644  *stemsnap = NULL;
645  }
646 
647  *stemsnapcnt = next;
648  free(values);
649 
650  /* I assume ascending order here and in normalize_stems(). */
651  qsort(*stemsnap, *stemsnapcnt, sizeof(StdStem), SortStems);
652  }
653 
654  /* No StdW, but StemSnap exists? */
655  if (stdw->width == -1 && *stemsnap != NULL) {
656  cnt = *stemsnapcnt;
657  i = cnt/2;
658  stdw->width = (*stemsnap)[i].width;
659  memmove((*stemsnap)+i, (*stemsnap)+i+1, cnt-i-1);
660 
661  if (--(*stemsnapcnt) == 0) {
662  free(*stemsnap);
663  *stemsnap = NULL;
664  }
665  }
666 }
667 
668 /* Assign CVT indices to blues and stems in global instructing context. In case
669  * we can't implant it because of already existent cvt table, reassign the cvt
670  * indices, picking them from existing cvt table (thus a cvt value can't be
671  * considered 'horizontal' or 'vertical', and reliable stem normalization is
672  * thus impossible) and adding some for new values.
673  */
674 static void init_cvt(GlobalInstrCt *gic) {
675  int i, cvtindex, cvtsize;
676  struct ttf_table *tab;
677  uint8 *cvt;
678 
679  cvtsize = 1;
680  if (gic->stdhw.width != -1) cvtsize++;
681  if (gic->stdvw.width != -1) cvtsize++;
682  cvtsize += gic->stemsnaphcnt;
683  cvtsize += gic->stemsnapvcnt;
684  cvtsize += gic->bluecnt * 2; /* possible family blues */
685 
686  cvt = calloc(cvtsize, cvtsize * sizeof(int16));
687  cvtindex = 0;
688 
689  /* Assign cvt indices */
690  for (i=0; i<gic->bluecnt; i++) {
691  gic->blues[i].cvtindex = cvtindex;
692  memputshort(cvt, 2*cvtindex++, rint(gic->blues[i].base));
693 
694  if (isfinite(gic->blues[i].family_base)) {
695  gic->blues[i].family_cvtindex = cvtindex;
696  memputshort(cvt, 2*cvtindex++, rint(gic->blues[i].family_base));
697  }
698  }
699 
700  if (gic->stdhw.width != -1) {
701  gic->stdhw.cvtindex = cvtindex;
702  memputshort(cvt, 2*cvtindex++, rint(gic->stdhw.width));
703  }
704 
705  for (i=0; i<gic->stemsnaphcnt; i++) {
706  gic->stemsnaph[i].cvtindex = cvtindex;
707  memputshort(cvt, 2*cvtindex++, rint(gic->stemsnaph[i].width));
708  }
709 
710  if (gic->stdvw.width != -1) {
711  gic->stdvw.cvtindex = cvtindex;
712  memputshort(cvt, 2*cvtindex++, rint(gic->stdvw.width));
713  }
714 
715  for (i=0; i<gic->stemsnapvcnt; i++) {
716  gic->stemsnapv[i].cvtindex = cvtindex;
717  memputshort(cvt, 2*cvtindex++, rint(gic->stemsnapv[i].width));
718  }
719 
720  cvtsize = cvtindex;
721  cvt = realloc(cvt, cvtsize * sizeof(int16));
722 
723  /* Try to implant the new cvt table */
724  gic->cvt_done = 0;
725 
726  tab = SFFindTable(gic->sf, CHR('c','v','t',' '));
727 
728  if ( tab==NULL ) {
729  tab = chunkalloc(sizeof(struct ttf_table));
730  tab->next = gic->sf->ttf_tables;
731  gic->sf->ttf_tables = tab;
732  tab->tag = CHR('c','v','t',' ');
733 
734  tab->len = tab->maxlen = cvtsize * sizeof(int16);
735  if (tab->maxlen >256) tab->maxlen = 256;
736  tab->data = cvt;
737 
738  gic->cvt_done = 1;
739  }
740  else {
741  if (tab->len >= cvtsize * (int)sizeof(int16) &&
742  memcmp(cvt, tab->data, cvtsize * sizeof(int16)) == 0)
743  gic->cvt_done = 1;
744 
745  free(cvt);
746 
747  if (!gic->cvt_done) {
748  ff_post_error(_("Can't insert 'cvt'"),
749  _("There already exists a 'cvt' table, perhaps legacy. "
750  "FontForge can use it, but can't make any assumptions on "
751  "values stored there, so generated instructions will be of "
752  "lower quality. If legacy hinting is to be scrapped, it is "
753  "suggested to clear the `cvt` and repeat autoinstructing. "
754  ));
755  }
756  }
757 
758  if (gic->cvt_done)
759 return;
760 
761  /* Fallback mode starts here. */
762 
763  for (i=0; i<gic->bluecnt; i++)
764  gic->blues[i].cvtindex =
765  TTF_getcvtval(gic->sf, gic->blues[i].base);
766 
767  if (gic->stdhw.width != -1)
768  gic->stdhw.cvtindex =
769  TTF_getcvtval(gic->sf, gic->stdhw.width);
770 
771  for (i=0; i<gic->stemsnaphcnt; i++)
772  gic->stemsnaph[i].cvtindex =
773  TTF_getcvtval(gic->sf, gic->stemsnaph[i].width);
774 
775  if (gic->stdvw.width != -1)
776  gic->stdvw.cvtindex =
777  TTF_getcvtval(gic->sf, gic->stdvw.width);
778 
779  for (i=0; i<gic->stemsnapvcnt; i++)
780  gic->stemsnapv[i].cvtindex =
781  TTF_getcvtval(gic->sf, gic->stemsnapv[i].width);
782 }
783 
784 /* We'll need at least STACK_DEPTH stack levels and a twilight point (and thus
785  * also a twilight zone). We also currently define some functions in fpgm.
786  * We must ensure this is indicated in the 'maxp' table.
787  *
788  * We also need two storage cells. As we now use SPVFS to set projection
789  * vector for diagonal hinting, we have to adjust values taken by SPVFS,
790  * so that diagonals look cleanly in all aspect ratios. Adjustments are
791  * not trivial to compute, so we do this once (in prep) and store them
792  * in storage[0] (for X direction) and storage[1] (for Y direction).
793  */
794 static void init_maxp(GlobalInstrCt *gic) {
795  struct ttf_table *tab = SFFindTable(gic->sf, CHR('m','a','x','p'));
796  uint16 zones, twpts, store, fdefs, stack;
797 
798  if ( tab==NULL ) {
799  tab = chunkalloc(sizeof(struct ttf_table));
800  tab->next = gic->sf->ttf_tables;
801  gic->sf->ttf_tables = tab;
802  tab->tag = CHR('m','a','x','p');
803  }
804 
805  if ( tab->len<32 ) {
806  tab->data = realloc(tab->data,32);
807  memset(tab->data+tab->len,0,32-tab->len);
808  tab->len = tab->maxlen = 32;
809  }
810 
811  zones = memushort(tab->data, 32, 7*sizeof(uint16));
812  twpts = memushort(tab->data, 32, 8*sizeof(uint16));
813  store = memushort(tab->data, 32, 9*sizeof(uint16));
814  fdefs = memushort(tab->data, 32, 10*sizeof(uint16));
815  stack = memushort(tab->data, 32, 12*sizeof(uint16));
816 
817  if (gic->fpgm_done && zones<2) zones=2;
818  if (gic->fpgm_done && twpts<1) twpts=1;
819  if (gic->fpgm_done && gic->prep_done && store<2) store=2;
820  if (gic->fpgm_done && fdefs<22) fdefs=22;
822 
823  memputshort(tab->data, 7*sizeof(uint16), zones);
824  memputshort(tab->data, 8*sizeof(uint16), twpts);
825  memputshort(tab->data, 9*sizeof(uint16), store);
826  memputshort(tab->data,10*sizeof(uint16), fdefs);
827  memputshort(tab->data,12*sizeof(uint16), stack);
828 }
829 
830 /* Other hinting software puts certain actions in FPGM to ease developer's life
831  * and compress the code. I feel that having a 'standard' library of functions
832  * could also help FF users.
833  *
834  * Caution! This code is heavily relied by autohinting. Any other code should
835  * be placed below it. It's good to first clear font's hinting tables, then
836  * autohint it, and then insert user's own code and do the manual hinting of
837  * glyphs that do need it.
838  */
839 static void init_fpgm(GlobalInstrCt *gic) {
840  uint8 new_fpgm[] =
841  {
842  /* Function 0: position a point within a blue zone (given via cvt).
843  * Note: in case of successful init of 'cvt' and 'prep' this function
844  * could be much simpler.
845  * Syntax: PUSHB_3 point cvt_of_blue 0 CALL
846  */
847  0xb0, // PUSHB_1
848  0x00, // 0
849  0x2c, // FDEF
850  0xb0, // PUSHB_1
851  0x00, // 0
852  0x13, // SZP0
853  0x4b, // MPPEM
854  0xb0, // PUSHB_1 - under this ppem blues will be specially rounded
855  GetBlueScale(gic->sf),
856  0x50, // LT
857  0x58, // IF
858  0xb0, // PUSHB_0
859  0x4a, // 74
860  0x76, // SROUND - round blues a bit up to grid
861  0x59, // EIF
862  0xb0, // PUSHB_1
863  0x00, // 0
864  0x23, // SWAP
865  0x3f, // MIAP[rnd] - blue zone positioned here
866  0x18, // RTG - round state for overshoots in monochrome mode
867  0xb0, // PUSHB_1
868  0x06, // 6
869  0x2b, // CALL
870  0x58, // IF
871  0x3d, // RTDG - round state for overshoots in antialiased mode
872  0x59, // EIF
873  0x4b, // MPPEM
874  0xb0, // PUSHB_1 - under following ppem overshoots will be suppressed
875  GetBlueScale(gic->sf),
876  0x50, // LT
877  0x58, // IF
878  0x7d, // RDTG - suppress overshoots
879  0x59, // EIF
880  0x20, // DUP
881  0xd4, // MDRP[rp0,rnd,grey]
882  0xb0, // PUSHB_1
883  0x01, // 1
884  0x13, // SZP0
885  0x2e, // MDAP[no-rnd]
886  0x18, // RTG
887  0x2d, // ENDF
888 
889  /* Function 1: Place given point relatively to previous, maintaining the
890  * minimum distance. Then call FPGM 12 to check if the point's gridfitted
891  * position is too far from its original position, and correct it, if necessary.
892  * Syntax: PUSB_2 point 1 CALL
893  */
894  0xb0, // PUSHB_1
895  0x01, // 1
896  0x2c, // FDEF
897  0x20, // DUP
898  0xda, // MDRP[rp0,min,white]
899  0xb0, // PUSHB_1
900  0x0c, // 12
901  0x2b, // CALL
902  0x2d, // ENDF
903 
904  /* Function 2: Below given ppem, substitute the width with cvt entry.
905  * Leave the resulting width on the stack. Used as the first step in
906  * normalizing cvt stems, see normalize_stem().
907  * Syntax: PUSHX_3 width cvt_index ppem 2 CALL
908  */
909  0xb0, // PUSHB_1
910  0x02, // 2
911  0x2c, // FDEF
912  0x4b, // MPPEM
913  0x52, // GT
914  0x58, // IF
915  0x45, // RCVT
916  0x23, // SWAP
917  0x59, // EIF
918  0x21, // POP
919  0x2d, // ENDF
920 
921  /* Function 3: round a stack element as a black distance, respecting
922  * minimum distance of 1px. This is used for rounding stems after width
923  * normalization. Often preceeded with SROUND, so finally sets RTG.
924  * Leaves the rounded width on the stack.
925  * Syntax: PUSHX_2 width_to_be_rounded 3 CALL
926  */
927  0xb0, // PUSHB_1
928  0x03, // 3
929  0x2c, // FDEF
930  0x69, // ROUND[black]
931  0x18, // RTG
932  0x20, // DUP
933  0xb0, // PUSHB_1
934  0x40, // 64, that's one pixel as F26Dot6
935  0x50, // LT
936  0x58, // IF
937  0x21, // POP
938  0xb0, // PUSHB_1
939  0x40, // 64
940  0x59, // EIF
941  0x2d, // ENDF
942 
943  /* Function 4: Position the second edge of a stem that is not normally
944  * regularized via cvt (but we snap it to cvt width below given ppem).
945  * Vertical stems need special round state when not snapped to cvt
946  * (basically, they are shortened by 0.25px before being rounded).
947  * Syntax: PUSHX_5 pt cvt_index chg_rp0 ppem 4 CALL
948  */
949  0xb0, // PUSHB_1
950  0x04, // 4
951  0x2c, // FDEF
952  0xb0, // PUSHB_1
953  0x06, // 6
954  0x2b, // CALL
955  0x58, // IF
956  0x21, // POP
957  0x23, // SWAP
958  0x21, // POP
959  0x7a, // ROFF
960  0x58, // IF
961  0xdd, // MDRP[rp0,min,rnd,black]
962  0x1b, // ELSE
963  0xcd, // MDRP[min,rnd,black]
964  0x59, // EIF
965  0x1b, // ELSE
966  0x4b, // MPPEM
967  0x52, // GT
968  0x58, // IF
969  0x58, // IF
970  0xfd, // MIRP[rp0,min,rnd,black]
971  0x1b, // ELSE
972  0xed, // MIRP[min,rnd,black]
973  0x59, // EIF
974  0x1b, // ELSE
975  0x23, // SWAP
976  0x21, // POP
977  0xb0, // PUSHB_1
978  0x05, // 5
979  0x2b, // CALL
980  0x58, // IF
981  0xb0, // PUSHB_1
982  0x46, // 70
983  0x76, // SROUND
984  0x59, // EIF
985  0x58, // IF
986  0xdd, // MDRP[rp0,min,rnd,black]
987  0x1b, // ELSE
988  0xcd, // MDRP[min,rnd,black]
989  0x59, // EIF
990  0x59, // EIF
991  0x59, // EIF
992  0x18, // RTG
993  0x2d, // ENDF
994 
995  /* Function 5: determine if we are hinting vertically. The function
996  * is crude and it's use is limited to conditions set by SVTCA[].
997  * Syntax: PUSHB_1 5 CALL; leaves boolean on the stack.
998  */
999  0xb0, // PUSHB_1
1000  0x05, // 5
1001  0x2c, // FDEF
1002  0x0d, // GFV
1003  0x5c, // NOT
1004  0x5a, // AND
1005  0x2d, // ENDF
1006 
1007  /* Function 6: check if we are hinting in grayscale.
1008  * CAUTION! Older FreeType versions lie if asked.
1009  * Syntax: PUSHB_1 6 CALL; leaves boolean on the stack.
1010  */
1011  0xb0, // PUSHB_1
1012  0x06, // 6
1013  0x2c, // FDEF
1014  0xb1, // PUSHB_2
1015  0x22, // 34
1016  0x01, // 1
1017  0x88, // GETINFO
1018  0x50, // LT
1019  0x58, // IF
1020  0xb0, // PUSHB_1
1021  0x20, // 32
1022  0x88, // GETINFO
1023  0x5c, // NOT
1024  0x5c, // NOT
1025  0x1b, // ELSE
1026  0xb0, // PUSHB_1
1027  0x00, // 0
1028  0x59, // EIF
1029  0x2d, // ENDF
1030 
1031  /* Function 7: check if we are hinting in cleartype.
1032  * CAUTION! FreeType doesn't support that, as subpixel
1033  * filtering is usually done by higher level library.
1034  * Syntax: PUSHB_1 7 CALL; leaves boolean on the stack.
1035  */
1036  0xb0, // PUSHB_1
1037  0x07, // 7
1038  0x2c, // FDEF
1039  0xb1, // PUSHB_2
1040  0x24, // 36
1041  0x01, // 1
1042  0x88, // GETINFO
1043  0x50, // LT
1044  0x58, // IF
1045  0xb0, // PUSHB_1
1046  0x40, // 64
1047  0x88, // GETINFO
1048  0x5c, // NOT
1049  0x5c, // NOT
1050  0x1b, // ELSE
1051  0xb0, // PUSHB_1
1052  0x00, // 0
1053  0x59, // EIF
1054  0x2d, // ENDF
1055 
1056  /* Function 8: Interpolate a point between
1057  * two other points and snap it to the grid.
1058  * Syntax: PUSHX_4 pt_to_ip rp1 rp2 8 CALL;
1059  */
1060  0xb0, // PUSHB_1
1061  0x08, // 8
1062  0x2c, // FDEF
1063  0x12, // SRP2
1064  0x11, // SRP1
1065  0x20, // DUP
1066  0x39, // IP
1067  0x2f, // MDAP[rnd]
1068  0x2d, // ENDF
1069 
1070  /* Function 9: Link a serif-like element edge to the opposite
1071  * edge of the base stem when rounding down to grid, but ensure
1072  * that its distance from the reference point is larger than
1073  * the base stem width at least to a specified amount of pixels.
1074  * Syntax: PUSHX_3 min_dist inner_pt outer_pt CALL;
1075  */
1076  0xb0, // PUSHB_1
1077  0x09, // 9
1078  0x2c, // FDEF
1079  0x20, // DUP
1080  0x7d, // RDTG
1081  0xb0, // PUSHB_1
1082  0x06, // 6
1083  0x2b, // CALL
1084  0x58, // IF
1085  0xc4, // MDRP[min,grey]
1086  0x1b, // ELSE
1087  0xcd, // MDRP[min,rnd,black]
1088  0x59, // EIF
1089  0x20, // DUP
1090  0xb0, // PUSHB_1
1091  0x03, // 3
1092  0x25, // CINDEX
1093  0x49, // MD[grid]
1094  0x23, // SWAP
1095  0x20, // DUP
1096  0xb0, // PUSHB_1
1097  0x04, // 4
1098  0x26, // MINDEX
1099  0x4a, // MD[orig]
1100  0xb0, // PUSHB_1
1101  0x00, // 0
1102  0x50, // LT
1103  0x58, // IF
1104  0x8a, // ROLL
1105  0x65, // NEG
1106  0x8a, // ROLL
1107  0x61, // SUB
1108  0x20, // DUP
1109  0xb0, // PUSHB_1
1110  0x00, // 0
1111  0x50, // LT
1112  0x58, // IF
1113  0x38, // SHPIX
1114  0x1b, // ELSE
1115  0x21, // POP
1116  0x21, // POP
1117  0x59, // EIF
1118  0x1b, // ELSE
1119  0x8a, // ROLL
1120  0x8a, // ROLL
1121  0x61, // SUB
1122  0x20, // DUP
1123  0xb0, // PUSHB_1
1124  0x00, // 0
1125  0x52, // GT
1126  0x58, // IF
1127  0x38, // SHPIX
1128  0x1b, // ELSE
1129  0x21, // POP
1130  0x21, // POP
1131  0x59, // EIF
1132  0x59, // EIF
1133  0x18, // RTG
1134  0x2d, // ENDF
1135 
1136  /* Function 10: depending from the hinting mode (grayscale or mono) set
1137  * rp0 either to pt1 or to pt2. This is used to link serif-like elements
1138  * either to the opposite side of the base stem or to the same side (i. e.
1139  * left-to-left and right-to-right).
1140  * Syntax: PUSHX_3 pt2 pt1 10 CALL
1141  */
1142  0xb0, // PUSHB_1
1143  0x0a, // 10
1144  0x2c, // FDEF
1145  0xb0, // PUSHB_1
1146  0x06, // 6
1147  0x2b, // CALL
1148  0x58, // IF
1149  0x21, // POP
1150  0x10, // SRP0
1151  0x1b, // ELSE
1152  0x10, // SRP0
1153  0x21, // POP
1154  0x59, // EIF
1155  0x2d, // ENDF
1156 
1157  /* Function 11: similar to FPGM 1, but places a point without
1158  * maintaining the minimum distance.
1159  * Syntax: PUSHX_2 point 11 CALL
1160  */
1161  0xb0, // PUSHB_1
1162  0x0b, // 11
1163  0x2c, // FDEF
1164  0x20, // DUP
1165  0xd2, // MDRP[rp0,white]
1166  0xb0, // PUSHB_1
1167  0x0c, // 12
1168  0x2b, // CALL
1169  0x2d, // ENDF
1170 
1171  /* Function 12: Check if the gridfitted position of the point is too far
1172  * from its original position, and shift it, if necessary. The function is
1173  * used to place vertical stems, it assures almost linear advance width
1174  * to PPEM scaling. Shift amount is capped to at most 1 px to prevent some
1175  * weird artifacts at very small ppems. In cleartype mode, no shift
1176  * is made at all.
1177  * Syntax: PUSHX_2 point 12 CALL
1178  */
1179  0xb0, // PUSHB_1
1180  0x0c, // 12
1181  0x2c, // FDEF
1182  0x20, // DUP
1183  0x2f, // MDAP[rnd], this is needed for grayscale mode
1184  0xb0, // PUSHB_1
1185  0x07, // 7
1186  0x2b, // CALL
1187  0x5c, // NOT
1188  0x58, // IF
1189  0x20, // DUP
1190  0x20, // DUP
1191  0x47, // GC[cur]
1192  0x23, // SWAP
1193  0x46, // GC[orig]
1194  0x61, // SUB
1195  0x6a, // ROUND[white]
1196  0x20, // DUP
1197  0x58, // IF
1198  0x20, // DUP
1199  0x64, // ABS
1200  0x62, // DIV
1201  0x38, // SHPIX
1202  0x1b, // ELSE
1203  0x21, // POP
1204  0x21, // POP
1205  0x59, // EIF
1206  0x1b, // ELSE
1207  0x21, // POP
1208  0x59, // EIF
1209  0x2d, // ENDF
1210 
1211  /* Function 13: Interpolate a HStem edge's reference point between two other points
1212  * and snap it to the grid. Then compare its new position with the ungridfitted
1213  * position of the second edge. If the gridfitted point belongs to the bottom edge
1214  * and now it is positioned above the top edge's original coordinate, then shift it
1215  * one pixel down; similarly, if the interpolation resulted in positioning the top
1216  * edge below the original coordinate of the bottom edge, shift it one pixel up.
1217  * Syntax: PUSHX_6 other_edge_refpt pt_to_ip rp1 rp2 13 CALL
1218  */
1219  0xb0, // PUSHB_1
1220  0x0d, // 13
1221  0x2c, // FDEF
1222  0x12, // SRP2
1223  0x11, // SRP1
1224  0x20, // DUP
1225  0x20, // DUP
1226  0x39, // IP
1227  0x2f, // MDAP[rnd]
1228  0x20, // DUP
1229  0x8a, // ROLL
1230  0x20, // DUP
1231  0x47, // GC[orig]
1232  0x8a, // ROLL
1233  0x46, // GC[cur]
1234  0x61, // SUB
1235  0x23, // SWAP
1236  0x8a, // ROLL
1237  0x20, // DUP
1238  0x8a, // ROLL
1239  0x23, // SWAP
1240  0x4A, // MD[orig]
1241  0xb0, // PUSHB_1
1242  0x00, // 0
1243  0x50, // LT
1244  0x58, // IF
1245  0x23, // SWAP
1246  0xb0, // PUSHB_1
1247  0x00, // 0
1248  0x52, // GT
1249  0x58, // IF
1250  0xb0, // PUSHB_1
1251  0x40, // 64
1252  0x38, // SHPIX
1253  0x1b, // ELSE
1254  0x21, // POP
1255  0x59, // EIF
1256  0x1b, // ELSE
1257  0x23, // SWAP
1258  0xb0, // PUSHB_1
1259  0x00, // 0
1260  0x50, // LT
1261  0x58, // IF
1262  0xb0, // PUSHB_1
1263  0x40, // 64
1264  0x65, // NEG
1265  0x38, // SHPIX
1266  0x1b, // ELSE
1267  0x21, // POP
1268  0x59, // EIF
1269  0x59, // EIF
1270  0x2d, // ENDF
1271 
1272  /* Function 14: Link two points using MDRP without maintaining
1273  * the minimum distance. In antialiased mode use rounding to
1274  * double grid for this operation, otherwise ensure there is no
1275  * distance between those two points below the given PPEM (i. e.
1276  * points are aligned). The function is used for linking nested
1277  * stems to each other, and guarantees their relative positioning
1278  * is preserved in the gridfitted outline.
1279  * Syntax: PUSHX_4 ppem ref_pt base_pt 14 CALL;
1280  */
1281  0xb0, // PUSHB_1
1282  0x0e, // 14
1283  0x2c, // FDEF
1284  0xb0, // PUSHB_1
1285  0x06, // 6
1286  0x2b, // CALL
1287  0x58, // IF
1288  0x3d, // RTDG
1289  0xd6, // MDRP[rp0,rnd,white]
1290  0x18, // RTG
1291  0x21, // POP
1292  0x21, // POP
1293  0x1b, // ELSE
1294  0x20, // DUP
1295  0xd6, // MDRP[rp0,rnd,white]
1296  0x8a, // ROLL
1297  0x4b, // MPPEM
1298  0x52, // GT
1299  0x58, // IF
1300  0x20, // DUP
1301  0x8a, // ROLL
1302  0x23, // SWAP
1303  0x49, // MD[grid]
1304  0x20, // DUP
1305  0xb0, // PUSHB_1
1306  0x00, // 0
1307  0x55, // NEQ
1308  0x58, // IF
1309  0x38, // SHPIX
1310  0x1b, // ELSE
1311  0x21, // POP
1312  0x21, // POP
1313  0x59, // EIF
1314  0x1b, // ELSE
1315  0x21, // POP
1316  0x21, // POP
1317  0x59, // EIF
1318  0x59, // EIF
1319  0x2d, // ENDF
1320 
1321  /* Function 15: similar to FPGM 1, but used to position a stem
1322  * relatively to the previous stem preserving the counter width
1323  * equal to the distance between another pair of previously positioned
1324  * stems. Thus it serves nearly the same purpose as PS counter hints.
1325  * Syntax: PUSHX_6 master_counter_start_pt master_counter_end_pt
1326  * current_counter_start_pt current_counter_end_pt ppem 15 CALL;
1327  */
1328  0xb0, // PUSHB_1
1329  0x0f, // 15
1330  0x2c, // FDEF
1331  0x23, // SWAP
1332  0x20, // DUP
1333  0xd6, // MDRP[rp0,rnd,white]
1334  0x20, // DUP
1335  0x2f, // MDAP[rnd], this is needed for grayscale mode
1336  0xb0, // PUSHB_1
1337  0x07, // 7
1338  0x2b, // CALL
1339  0x5c, // NOT
1340  0x58, // IF
1341  0x23, // SWAP
1342  0x20, // DUP
1343  0x58, // IF
1344  0x4b, // MPPEM
1345  0x53, // GTEQ
1346  0x1b, // ELSE
1347  0x21, // POP
1348  0xb0, // PUSHB_1
1349  0x01, // 1
1350  0x59, // EIF
1351  0x58, // IF
1352  0x8a, // ROLL
1353  0xb0, // PUSHB_1
1354  0x04, // 4
1355  0x26, // MINDEX
1356  0x49, // MD[grid]
1357  0x23, // SWAP
1358  0x8a, // ROLL
1359  0x23, // SWAP
1360  0x20, // DUP
1361  0x8a, // ROLL
1362  0x49, // MD[grid]
1363  0x8a, // ROLL
1364  0x23, // SWAP
1365  0x61, // SUB
1366  0x38, // SHPIX
1367  0x1b, // ELSE
1368  0x21, // POP
1369  0x21, // POP
1370  0x21, // POP
1371  0x21, // POP
1372  0x59, // EIF
1373  0x1b, // ELSE
1374  0x21, // POP
1375  0x21, // POP
1376  0x21, // POP
1377  0x21, // POP
1378  0x21, // POP
1379  0x59, // EIF
1380  0x2d, // ENDF
1381 
1382  /* Function 16: Same as FPGM 1, but calls FPGM 18 rather than FPGM 12
1383  * and thus takes 3 arguments.
1384  * Syntax: PUSHX_3 ref_point point 16 CALL
1385  */
1386  0xb0, // PUSHB_1
1387  0x10, // 16
1388  0x2c, // FDEF
1389  0x20, // DUP
1390  0xda, // MDRP[rp0,min,white]
1391  0xb0, // PUSHB_1
1392  0x12, // 18
1393  0x2b, // CALL
1394  0x2d, // ENDF
1395 
1396  /* Function 17: Same as FPGM 11, but calls FPGM 18 rather than FPGM 12
1397  * and thus takes 3 arguments.
1398  * Syntax: PUSHX_3 ref_point point 17 CALL
1399  */
1400  0xb0, // PUSHB_1
1401  0x11, // 17
1402  0x2c, // FDEF
1403  0x20, // DUP
1404  0xd2, // MDRP[rp0,white]
1405  0xb0, // PUSHB_1
1406  0x12, // 18
1407  0x2b, // CALL
1408  0x2d, // ENDF
1409 
1410  /* Function 18: this is a special version of FPGM 12, used when the counter
1411  * control is enabled but doesn't directly affect the stem which is going to
1412  * be positioned. Unlike FPGM 12, it doesn't just attempt to position a point
1413  * closely enough to its original coordinate, but also checks if the previous
1414  * stem has already been shifted relatively to its "ideal" position FPGM 12 would
1415  * determine. If so, then the desired point position is corrected relatively to
1416  * the current placement of the previous stem.
1417  * Syntax: PUSHX_3 ref_point point 18 CALL
1418  */
1419  0xb0, // PUSHB_1
1420  0x12, // 18
1421  0x2c, // FDEF
1422  0x20, // DUP
1423  0x2f, // MDAP[rnd], this is needed for grayscale mode
1424  0xb0, // PUSHB_1
1425  0x07, // 7
1426  0x2b, // CALL
1427  0x5c, // NOT
1428  0x58, // IF
1429  0x20, // DUP
1430  0x20, // DUP
1431  0x47, // GC[cur]
1432  0x23, // SWAP
1433  0x46, // GC[orig]
1434  0x61, // SUB
1435  0x6a, // ROUND[white]
1436  0x8a, // ROLL
1437  0x20, // DUP
1438  0x47, // GC[cur]
1439  0x23, // SWAP
1440  0x46, // GC[orig]
1441  0x23, // SWAP
1442  0x61, // SUB
1443  0x6a, // ROUND[white]
1444  0x60, // ADD
1445  0x20, // DUP
1446  0x58, // IF
1447  0x20, // DUP
1448  0x64, // ABS
1449  0x62, // DIV
1450  0x38, // SHPIX
1451  0x1b, // ELSE
1452  0x21, // POP
1453  0x21, // POP
1454  0x59, // EIF
1455  0x1b, // ELSE
1456  0x21, // POP
1457  0x21, // POP
1458  0x59, // EIF
1459  0x2d, // ENDF
1460 
1461  /* Function 19: used to align a point relatively to a diagonal line,
1462  * specified by two other points. First we check if the point going
1463  * to be positioned doesn't deviate too far from the line in the original
1464  * outline. If the deviation is small enough to neglect it, we use ALIGNRP
1465  * to position the point, otherwise MDRP is used instead. We can't just
1466  * always use MDRP, because this command may produce wrong results at
1467  * small PPEMs, if the original and gridfitted coordinates of the line end
1468  * points specify slightly different unit vectors.
1469  * Syntax: point diag_start_point diag_end_point 19 CALL
1470  */
1471  0xb0, // PUSHB_1
1472  0x13, // 19
1473  0x2c, // FDEF
1474  0x20, // DUP
1475  0x8a, // ROLL
1476  0x20, // DUP
1477  0x8a, // ROLL
1478  0x87, // SDPVTL[orthogonal]
1479  0x20, // DUP
1480  0xb0, // PUSHB_1
1481  0x03, // 4
1482  0x25, // CINDEX
1483  0x4a, // MD[orig]
1484  0x64, // ABS
1485  0x23, // SWAP
1486  0x8a, // ROLL
1487  0x07, // SPVTL[orthogonal]
1488  0xb0, // PUSHB_1
1489  0x20, // 32
1490  0x50, // LT
1491  0x58, // IF
1492  0x3c, // ALIGNRP
1493  0x1b, // ELSE
1494  0xc0, // MDRP[grey]
1495  0x59, // EIF
1496  0x2d, // ENDF
1497 
1498  /* Function 20: compute adjustments for X and Y components of projection
1499  * vector, for aspect ratios different than 1:1, and store them
1500  * in storage[0] and storage[1] respectively.
1501  * Syntax: 20 CALL (use it only ONCE, from PREP table).
1502  */
1503  0xb0, // PUSHB_1
1504  0x14, // 20
1505  0x2c, // FDEF
1506  0xb3, // PUSHB_4 (we normally need no adjustments)
1507  0x00, // 0
1508  0x40, // 1.0 (F26Dot6)
1509  0x01, // 1
1510  0x40, // 1.0 (F26Dot6)
1511  0x42, // WS
1512  0x42, // WS
1513  0x01, // SVTCA[x-axis]
1514  0x4b, // MPPEM
1515  0xb8, // PUSHW_1
1516  0x10, // 4096
1517  0x00, // ...still that 4096
1518  0x63, // MUL (so we have PPEM along X casted to F26Dot6)
1519  0x00, // SVTCA[y-axis]
1520  0x4b, // MPPEM
1521  0xb8, // PUSHW_1
1522  0x10, // 4096
1523  0x00, // ...still that 4096
1524  0x63, // MUL (so we have PPEM along Y casted to F26Dot6)
1525  0x20, // DUP
1526  0x8a, // ROLL
1527  0x20, // DUP
1528  0x8a, // ROLL
1529  0x55, // NEQ
1530  0x58, // IF (if PPEM along X != PPEM along Y)
1531  0x20, // DUP
1532  0x8a, // ROLL
1533  0x20, // DUP
1534  0x8a, // ROLL
1535  0x52, // GT
1536  0x58, // IF (if PPEM along X < PPEM along Y)
1537  0x23, // SWAP
1538  0x62, // DIV
1539  0x20, // DUP
1540  0xb0, // PUSHB_1
1541  0x00, // 0
1542  0x23, // SWAP
1543  0x42, // WS
1544  0x1b, // ELSE (if PPEM along X > PPEM along Y)
1545  0x62, // DIV
1546  0x20, // DUP
1547  0xb0, // PUSHB_1
1548  0x01, // 1
1549  0x23, // SWAP
1550  0x42, // WS
1551  0x59, // EIF
1552  0x20, // DUP [A LOOP STARTS HERE]
1553  0xb0, // PUSHB_1
1554  0x40, // 1.0 (F26Dot6)
1555  0x52, // GT
1556  0x58, // IF (bigger adjustment is greater than 1.0 => needs fixing)
1557  0xb2, // PUSHB_3
1558  0x00, // 0
1559  0x20, // 0.5 (F26Dot6)
1560  0x00, // 0
1561  0x43, // RS
1562  0x63, // MUL
1563  0x42, // WS (we halved adjustment for X)
1564  0xb2, // PUSHB_3
1565  0x01, // 1
1566  0x20, // 0.5 (F26Dot6)
1567  0x01, // 1
1568  0x43, // RS
1569  0x63, // MUL
1570  0x42, // WS (we halved adjustment for Y)
1571  0xb0, // PUSHB_1
1572  0x20, // 0.5 (F26Dot6)
1573  0x63, // MUL (we halved the bigger adjustment)
1574  0xb0, // PUSHB_1
1575  0x19, // 25
1576  0x65, // NEG
1577  0x1c, // JMPR (go back to the start of the loop)
1578  0x21, // POP
1579  0x59, // EIF
1580  0x1b, // ELSE (if PPEM along X == PPEM along Y)
1581  0x21, // POP
1582  0x21, // POP
1583  0x59, // EIF
1584  0x2d, // ENDF
1585 
1586  /* Function 21: call it before SFVFS or SPVFS, so that the vector
1587  * passed is aspect-ratio corrected.
1588  * Syntax: x y 21 CALL
1589  */
1590  0xb0, // PUSHB_1
1591  0x15, // 21
1592  0x2c, // FDEF
1593  0xb0, // PUSHB_1
1594  0x01, // 1
1595  0x43, // RS
1596  0x63, // MUL
1597  0x23, // SWAP
1598  0xb0, // PUSHB_1
1599  0x00, // 0
1600  0x43, // RS
1601  0x63, // MUL
1602  0x23, // SWAP
1603  0x2d // ENDF
1604  };
1605 
1606  struct ttf_table *tab = SFFindTable(gic->sf, CHR('f','p','g','m'));
1607 
1608  if ( tab==NULL ) {
1609  /* We have to create such table. */
1610  tab = chunkalloc(sizeof(struct ttf_table));
1611  tab->next = gic->sf->ttf_tables;
1612  gic->sf->ttf_tables = tab;
1613  tab->tag = CHR('f','p','g','m');
1614  tab->len = 0;
1615  }
1616 
1617  if (tab->len==0 ||
1618  (tab->len < (int)sizeof(new_fpgm) &&
1619  !memcmp(tab->data, new_fpgm, tab->len)))
1620  {
1621  /* We can safely update font program. */
1622  tab->len = tab->maxlen = sizeof(new_fpgm);
1623  tab->data = realloc(tab->data, sizeof(new_fpgm));
1624  memmove(tab->data, new_fpgm, sizeof(new_fpgm));
1625  gic->fpgm_done = 1;
1626  }
1627  else {
1628  /* there already is a font program. */
1629  gic->fpgm_done = 0;
1630  if (tab->len >= (int)sizeof(new_fpgm))
1631  if (!memcmp(tab->data, new_fpgm, sizeof(new_fpgm)))
1632  gic->fpgm_done = 1; /* it's ours. */
1633 
1634  /* Log warning message. */
1635  if (!gic->fpgm_done)
1636  ff_post_error(_("Can't insert 'fpgm'"),
1637  _("There exists a 'fpgm' code that seems incompatible with "
1638  "FontForge's. Instructions generated will be of lower "
1639  "quality. If legacy hinting is to be scrapped, it is "
1640  "suggested to clear the `fpgm` and repeat autoinstructing. "
1641  "It will be then possible to append user's code to "
1642  "FontForge's 'fpgm', but due to possible future updates, "
1643  "it is extremely advised to use high numbers for user's "
1644  "functions."
1645  ));
1646  }
1647 }
1648 
1649 /* When initializing global instructing context, we want to set up the 'prep'
1650  * table in order to apply family blues and normalize stem widths for monochrome
1651  * display.
1652  *
1653  * The stem normalizer is heavily based on simple concept from FreeType2.
1654  *
1655  * First round the StdW. Then for each StemSnap (going outwards from StdW) check
1656  * if it's within 1px from its already rounded neighbor, and if so, snap it
1657  * before rounding. From all vertical stems (but not StdHW itself), 0.25px is
1658  * subtracted before rounding. Similar method is used for non-cvt stems, they're
1659  * snapped to the closest standard width if possible.
1660  *
1661  * NOTE: because of tiny scaling issues, we have to compute ppem at which each
1662  * stem stops being snapped to its already-rounded neighbor here instead of
1663  * relegating this to the truetype bytecide interpreter. We can't simply rely
1664  * on cvt cut-in.
1665  */
1666 
1667 static int compute_blue_height(real val, int EM, int bluescale, int ppem) {
1668  int scaled_val = rint((rint(fabs(val)) * ppem * 64)/EM);
1669  if (ppem < bluescale) scaled_val += 16;
1670 return (scaled_val + 32) / 64 * (val / fabs(val));
1671 }
1672 
1673 static uint8 *use_family_blues(uint8 *prep_head, GlobalInstrCt *gic) {
1674  int i, h1, h2, stopat;
1675  int bs = GetBlueScale(gic->sf);
1676  int EM = gic->sf->ascent + gic->sf->descent;
1677  int callargs[3];
1678 
1679  for (i=0; i<gic->bluecnt; i++) {
1680  if (isfinite(gic->blues[i].family_base))
1681  {
1682  for (stopat=0; stopat<32768; stopat++) {
1683  h1 = compute_blue_height(gic->blues[i].base, EM, bs, stopat);
1684  h2 = compute_blue_height(gic->blues[i].family_base, EM, bs, stopat);
1685  if (abs(h1 - h2) > 1) break;
1686  }
1687 
1688  callargs[0] = gic->blues[i].family_cvtindex;
1689  callargs[1] = stopat;
1690  callargs[2] = 2;
1691 
1692  prep_head = pushnum(prep_head, gic->blues[i].cvtindex);
1693  *prep_head++ = DUP;
1694  *prep_head++ = 0x45; //RCVT
1695  prep_head = pushnums(prep_head, 3, callargs);
1696  *prep_head++ = CALL;
1697  *prep_head++ = 0x44; //WCVTP
1698  }
1699  }
1700 
1701  return prep_head;
1702 }
1703 
1704 /* Return width (in pixels) of given stem, taking snaps into account.
1705  */
1706 #define SNAP_THRESHOLD (64)
1707 static int compute_stem_width(int xdir, StdStem *stem, int EM, int ppem) {
1708  int scaled_width; /* in 1/64th pixels */
1709  int snapto_width; /* in 1/64th pixels */
1710 
1711  scaled_width = (int)rint((rint(fabs(stem->width)) * ppem * 64.0)/EM);
1712  if (scaled_width < 64) scaled_width = 64;
1713 
1714  if (stem->snapto != NULL)
1715  {
1716  if (stem->stopat > ppem) {
1717  snapto_width = 64*compute_stem_width(xdir, stem->snapto, EM, ppem);
1718 
1719  if (abs(snapto_width - scaled_width) < SNAP_THRESHOLD)
1720  scaled_width = snapto_width;
1721  }
1722 
1723  if (xdir) scaled_width -= 16;
1724  }
1725 
1726 return (scaled_width + 32) / 64;
1727 }
1728 
1729 /* Normalize a single stem. The code generated assumes there is a scaled stem
1730  * width on bytecode interpreter's stack, and leaves normalized width there.
1731  */
1732 static uint8 *normalize_stem(uint8 *prep_head, int xdir, StdStem *stem, GlobalInstrCt *gic) {
1733  int callargs[3];
1734  int i;
1735 
1736  stem->stopat = 32767;
1737 
1738  if (stem->snapto != NULL)
1739  {
1740  /* compute ppem at which to stop snapping stem to stem->snapto */
1741  int EM = gic->sf->ascent + gic->sf->descent;
1742 
1743  for (i=7; i<32768; i++) {
1744  int width_parent = compute_stem_width(xdir, stem->snapto, EM, i);
1745  int width_me = compute_stem_width(xdir, stem, EM, i);
1746 
1747  if (width_parent != width_me) {
1748  stem->stopat = i;
1749  break;
1750  }
1751  }
1752 
1753  /* snap if below given ppem */
1754  callargs[0] = stem->snapto->cvtindex;
1755  callargs[1] = stem->stopat;
1756  callargs[2] = 2;
1757  prep_head = pushnums(prep_head, 3, callargs);
1758  *prep_head++ = CALL;
1759 
1760  /* Round[black], respecting minimum distance of 1 px */
1761  /* Vertical stems (but not StdVW) use special rounding threshold. */
1762  /* The rounding function restores default round state at the end. */
1763  if (xdir) {
1764  prep_head = push2nums(prep_head, 3, 70);
1765  *prep_head++ = SROUND;
1766  }
1767  else prep_head = pushnum(prep_head, 3);
1768 
1769  *prep_head++ = CALL;
1770  }
1771  else {
1772  /* simply round[black] respecting minimum distance of 1 px */
1773  prep_head = pushnum(prep_head, 3);
1774  *prep_head++ = CALL;
1775  }
1776 
1777 return prep_head;
1778 }
1779 
1780 /* Append the code for normalizing standard stems' widths to 'prep'.
1781  */
1782 static uint8 *normalize_stems(uint8 *prep_head, int xdir, GlobalInstrCt *gic) {
1783  int i, t;
1784  StdStem *mainstem = xdir?&(gic->stdvw):&(gic->stdhw);
1785  StdStem *otherstems = xdir?gic->stemsnapv:gic->stemsnaph;
1786  int otherstemcnt = xdir?gic->stemsnapvcnt:gic->stemsnaphcnt;
1787 
1788  if (mainstem->width == -1)
1789 return prep_head;
1790 
1791  /* set up the standard width */
1792  mainstem->snapto = NULL;
1793  *prep_head++ = xdir?SVTCA_x:SVTCA_y;
1794  prep_head = pushnum(prep_head, mainstem->cvtindex);
1795  *prep_head++ = DUP;
1796  *prep_head++ = 0x45; //RCVT
1797  prep_head = normalize_stem(prep_head, xdir, mainstem, gic);
1798  *prep_head++ = 0x44; //WCVTP
1799 
1800  /* set up other standard widths */
1801  for (i=0; i<otherstemcnt && otherstems[i].width < mainstem->width; i++);
1802  t = i-1;
1803 
1804  for (i=t; i>=0; i--) {
1805  otherstems[i].snapto = i==t?mainstem:otherstems+i+1;
1806  prep_head = pushnum(prep_head, otherstems[i].cvtindex);
1807  *prep_head++ = DUP;
1808  *prep_head++ = 0x45; //RCVT
1809  prep_head = normalize_stem(prep_head, xdir, otherstems+i, gic);
1810  *prep_head++ = 0x44; //WCVTP
1811  }
1812 
1813  for (i=t+1; i<otherstemcnt; i++) {
1814  otherstems[i].snapto = i==t+1?mainstem:otherstems+i-1;
1815  prep_head = pushnum(prep_head, otherstems[i].cvtindex);
1816  *prep_head++ = DUP;
1817  *prep_head++ = 0x45; //RCVT
1818  prep_head = normalize_stem(prep_head, xdir, otherstems+i, gic);
1819  *prep_head++ = 0x44; //WCVTP
1820  }
1821 
1822 return prep_head;
1823 }
1824 
1825 /* Turning dropout control on will dramatically improve mono rendering, even
1826  * without further hinting, especcialy for light typefaces. And turning hinting
1827  * off at veeery small pixel sizes is required, because hints tend to visually
1828  * tear outlines apart when not having enough workspace.
1829  *
1830  * We also normalize stem widths here, this usually massively improves overall
1831  * consistency. We currently do this only for monochrome rendering (this
1832  * includes WinXP's cleartype).
1833  *
1834  * TODO! We should take 'gasp' table into account and set up blues here.
1835  */
1836 static void init_prep(GlobalInstrCt *gic) {
1837  uint8 new_prep_preamble[] =
1838  {
1839  /* Enable dropout control. FreeType 2.3.7 need explicit SCANTYPE. */
1840  0xb8, // PUSHW_1
1841  0x01, // 511
1842  0xff, // ...still that 511
1843  0x85, // SCANCTRL
1844  0xb0, // PUSHB_1
1845  0x01, // 1
1846  0x8d, // SCANTYPE
1847 
1848  /* Measurements are taken along Y axis */
1849  0x00, // SVTCA[y-axis]
1850 
1851  /* Turn hinting off at very small pixel sizes */
1852  0x4b, // MPPEM
1853  0xb0, // PUSHB_1
1854  0x08, // 8 - hinting threshold - should be configurable
1855  0x50, // LT
1856  0x58, // IF
1857  0xb1, // PUSHB_2
1858  0x01, // 1
1859  0x01, // 1
1860  0x8e, // INSTCTRL
1861  0x59, // EIF
1862 
1863  /* Determine the cvt cut-in used */
1864  0xb1, // PUSHB_2
1865  0x46, // 70/64 = about 1.094 pixel (that's our default setting)
1866  0x06, // 6
1867  0x2b, // CALL
1868  0x58, // IF
1869  0x21, // POP
1870  0xb0, // PUSHB_1
1871  0x10, // 16/64 = 0.25 pixel (very low cut-in for grayscale mode)
1872  0x59, // EIF
1873  0x4b, // MPPEM
1874  0xb0, // PUSHB_1
1875  0x14, // 20 PPEM - a threshold below which we'll use larger CVT cut-in
1876  0x52, // GT
1877  0x58, // IF
1878  0x21, // POP
1879  0xb0, // PUSHB_1
1880  0x80, // 128/64 = 2 pixels (extreme regularization for small ppems)
1881  0x59, // EIF
1882  0x1d // SCVTCI
1883  };
1884 
1885  int preplen = sizeof(new_prep_preamble);
1886  int prepmaxlen = preplen;
1887  uint8 *new_prep, *prep_head;
1888  struct ttf_table *tab;
1889 
1890  if (gic->cvt_done) {
1891  prepmaxlen += 48 + 38*(gic->stemsnaphcnt + gic->stemsnapvcnt);
1892  prepmaxlen += 14*(gic->bluecnt);
1893  }
1894 
1895  if (gic->fpgm_done)
1896  prepmaxlen += 3;
1897 
1898  new_prep = calloc(prepmaxlen, sizeof(uint8));
1899  memmove(new_prep, new_prep_preamble, preplen*sizeof(uint8));
1900  prep_head = new_prep + preplen;
1901 
1902  if (gic->cvt_done && gic->fpgm_done) {
1903  /* Apply family blues. */
1904  prep_head = use_family_blues(prep_head, gic);
1905 
1906  /* Normalize stems (only in monochrome mode) */
1907  prep_head = pushnum(prep_head, 6);
1908  *prep_head++ = CALL;
1909  *prep_head++ = 0x5c; // NOT
1910  *prep_head++ = 0x58; // IF
1911  prep_head = normalize_stems(prep_head, 0, gic);
1912  prep_head = normalize_stems(prep_head, 1, gic);
1913  *prep_head++ = 0x59; // EIF
1914  }
1915 
1916  /* compute adjustments for projection vector */
1917  if (gic->fpgm_done) {
1918  prep_head = pushnum(prep_head, 20);
1919  *prep_head++ = CALL;
1920  }
1921 
1922  preplen = prep_head - new_prep;
1923 
1924  tab = SFFindTable(gic->sf, CHR('p','r','e','p'));
1925 
1926  if ( tab==NULL ) {
1927  /* We have to create such table. */
1928  tab = chunkalloc(sizeof(struct ttf_table));
1929  tab->next = gic->sf->ttf_tables;
1930  gic->sf->ttf_tables = tab;
1931  tab->tag = CHR('p','r','e','p');
1932  tab->len = 0;
1933  }
1934 
1935  if (tab->len==0 ||
1936  (tab->len < preplen && !memcmp(tab->data, new_prep, tab->len)))
1937  {
1938  /* We can safely update cvt program. */
1939  tab->len = tab->maxlen = preplen;
1940  tab->data = realloc(tab->data, preplen);
1941  memmove(tab->data, new_prep, preplen);
1942  gic->prep_done = 1;
1943  }
1944  else {
1945  /* there already is a font program. */
1946  gic->prep_done = 0;
1947  if (tab->len >= preplen)
1948  if (!memcmp(tab->data, new_prep, preplen))
1949  gic->prep_done = 1; /* it's ours */
1950 
1951  /* Log warning message. */
1952  if (!gic->prep_done)
1953  ff_post_error(_("Can't insert 'prep'"),
1954  _("There exists a 'prep' code incompatible with FontForge's. "
1955  "It can't be guaranteed it will work well. It is suggested "
1956  "to allow FontForge to insert its code and then append user"
1957  "'s own."
1958  ));
1959  }
1960 
1961  free(new_prep);
1962 }
1963 
1964 /*
1965  * Initialize Global Instructing Context
1966  */
1967 #define EDGE_FUZZ (500.0)
1969  BlueData *bd) {
1970  BlueData _bd;
1971 
1972  if (bd == NULL) {
1973  QuickBlues(sf,layer,&_bd);
1974  bd = &_bd;
1975  }
1976 
1977  gic->sf = sf;
1978  gic->bd = bd;
1979  gic->layer = layer;
1980  gic->fudge = (sf->ascent+sf->descent)/EDGE_FUZZ;
1981 
1982  gic->cvt_done = false;
1983  gic->fpgm_done = false;
1984  gic->prep_done = false;
1985 
1986  gic->bluecnt = 0;
1987  gic->stdhw.width = -1;
1988  gic->stemsnaph = NULL;
1989  gic->stemsnaphcnt = 0;
1990  gic->stdvw.width = -1;
1991  gic->stemsnapv = NULL;
1992  gic->stemsnapvcnt = 0;
1993 
1994  GICImportBlues(gic);
1995  GICImportStems(0, gic); /* horizontal stems */
1996  GICImportStems(1, gic); /* vertical stems */
1997 
1998  init_cvt(gic);
1999  init_fpgm(gic);
2000  init_prep(gic);
2001  init_maxp(gic);
2002 }
2003 
2004 /*
2005  * Finalize Global Instructing Context
2006  */
2008  gic->sf = NULL;
2009  gic->bd = NULL;
2010  gic->fudge = 0;
2011 
2012  gic->cvt_done = false;
2013  gic->fpgm_done = false;
2014  gic->prep_done = false;
2015 
2016  gic->bluecnt = 0;
2017  gic->stdhw.width = -1;
2018  if (gic->stemsnaphcnt != 0) free(gic->stemsnaph);
2019  gic->stemsnaphcnt = 0;
2020  gic->stemsnaph = NULL;
2021  gic->stdvw.width = -1;
2022  if (gic->stemsnapvcnt != 0) free(gic->stemsnapv);
2023  gic->stemsnapvcnt = 0;
2024  gic->stemsnapv = NULL;
2025 }
2026 
2027 /******************************************************************************
2028  ******************************************************************************
2029  **
2030  ** Stuff for managing global instructing context ends here. Now we'll deal
2031  ** with single glyphs.
2032  **
2033  ** Many functions here need large or similar sets of arguments. I decided to
2034  ** define an '(local) instructing context' to have them in one place and keep
2035  ** functions' argument lists reasonably short. I first need to define some
2036  ** internal sub-structures for instructing diagonal stems. Similar structures
2037  ** for CVT management (based on PS Private) are defined in splinefont.h, and
2038  ** were initialized handled above.
2039  **
2040  ******************************************************************************
2041  ******************************************************************************/
2042 
2043 /* A line, described by two points */
2044 typedef struct pointvector {
2046  int done;
2048 
2049 /* In this structure we store information about diagonales,
2050  relatively to which the given point should be positioned */
2051 typedef struct diagpointinfo {
2052  struct pointvector line[2];
2053  int count;
2055 
2056 typedef struct instrct {
2057  /* Things that are global for font and should be
2058  initialized before instructing particular glyph. */
2060 
2061  /* Here things for this particular glyph start. */
2064 
2065  /* instructions */
2066  uint8 *instrs; /* the beginning of the instructions */
2067  uint8 *pt; /* the current position in the instructions */
2068 
2069  /* properties indexed by contour number */
2070  int *contourends; /* points ending their contours. Null-terminated. */
2071  uint8 *clockwise; /* is given contour clockwise? */
2072 
2073  /* properties, indexed by ttf point index. Some could be compressed. */
2074  int ptcnt; /* number of points in this glyph */
2075  BasePoint *bp; /* point coordinates */
2076  uint8 *touched; /* touchflags; points explicitly instructed */
2077  uint8 *affected; /* touchflags; almost touched, but optimized out */
2078 
2079  /* data from stem detector */
2081 
2082  /* stuff for hinting diagonals */
2083  int diagcnt;
2085  DiagPointInfo *diagpts; /* indexed by ttf point index */
2086 
2087  /* stuff for hinting edges (stems, blues, strong point interpolation). */
2088  int xdir; /* direction flag: x=true, y=false */
2089  int cdir; /* is current contour outer? - blues need this */
2090  struct __edge {
2091  real base; /* where the edge is */
2092  int refpt; /* best ref. point for an edge, ttf index, -1 if none */
2093  int refscore; /* its quality, for searching better one; 0 if none */
2094  int othercnt; /* count of other points to instruct for this edge */
2095  int *others; /* their ttf indices, optimize_edge() is advised */
2096  } edge;
2097 
2098  /* Some variables for tracking graphics state */
2099  int rp0;
2101 
2102 /******************************************************************************
2103  *
2104  * Low-level routines for manipulting and classifying splinepoints
2105  *
2106  ******************************************************************************/
2107 
2108 /* Find previous point index on the contour. */
2109 static int PrevOnContour(int *contourends, int p) {
2110  int i;
2111 
2112  if (p == 0) return contourends[0];
2113  else {
2114  for (i=0; contourends[i+1]; i++)
2115  if (contourends[i]+1 == p)
2116  return contourends[i+1];
2117 
2118  return p-1;
2119  }
2120 }
2121 
2122 /* Find next point index on the contour. */
2123 static int NextOnContour(int *contourends, int p) {
2124  int i;
2125 
2126  if (p == 0) return 1;
2127  else {
2128  for (i=0; contourends[i]; i++) {
2129  if (contourends[i] == p) {
2130  if (i==0) return 0;
2131  else return contourends[i-1]+1;
2132  }
2133  }
2134  return p+1;
2135  }
2136 }
2137 
2138 /* For hinting stems, I found it needed to check if candidate point for
2139  * instructing is pararell to hint's direction to avoid snapping wrong points.
2140  * I splitted the routine into two, as sometimes it may be needed to check
2141  * the angle to be strictly almost the same, not just pararell.
2142  */
2143 static int __same_angle(int *contourends, BasePoint *bp, int p, double angle) {
2144  int PrevPoint, NextPoint;
2145  double PrevTangent, NextTangent;
2146 
2147  PrevPoint = PrevOnContour(contourends, p);
2148  NextPoint = NextOnContour(contourends, p);
2149  PrevTangent = atan2(bp[p].y - bp[PrevPoint].y, bp[p].x - bp[PrevPoint].x);
2150  NextTangent = atan2(bp[NextPoint].y - bp[p].y, bp[NextPoint].x - bp[p].x);
2151 
2152  /* If at least one of the tangents is close to the given angle, return */
2153  /* true. 'Close' means about 5 deg, i.e. about 0.087 rad. */
2154  PrevTangent = fabs(PrevTangent-angle);
2155  NextTangent = fabs(NextTangent-angle);
2156  while (PrevTangent > M_PI) PrevTangent -= 2*M_PI;
2157  while (NextTangent > M_PI) NextTangent -= 2*M_PI;
2158 return (fabs(PrevTangent) <= 0.087) || (fabs(NextTangent) <= 0.087);
2159 }
2160 
2161 static int same_angle(int *contourends, BasePoint *bp, int p, double angle) {
2162 return __same_angle(contourends, bp, p, angle) || __same_angle(contourends, bp, p, angle+M_PI);
2163 }
2164 
2165 /* I found it needed to write some simple functions to classify points snapped
2166  * to hint's edges. Classification helps to establish the most accurate leading
2167  * point for an edge.
2168  */
2169 static int _IsExtremum(int xdir, SplinePoint *sp) {
2170 return xdir?
2171  (!sp->nonextcp && !sp->noprevcp && sp->nextcp.x==sp->me.x && sp->prevcp.x==sp->me.x):
2172  (!sp->nonextcp && !sp->noprevcp && sp->nextcp.y==sp->me.y && sp->prevcp.y==sp->me.y);
2173 }
2174 
2175 static int IsExtremum(int xdir, int p, SplinePoint *sp) {
2176  int ret = _IsExtremum(xdir, sp);
2177 
2178  if ((sp->nextcpindex == p) && (sp->next != NULL) && (sp->next->to != NULL))
2179  ret = ret || _IsExtremum(xdir, sp->next->to);
2180  else if ((sp->ttfindex != p) && (sp->prev != NULL) && (sp->prev->from != NULL))
2181  ret = ret || _IsExtremum(xdir, sp->prev->from);
2182 
2183 return ret;
2184 }
2185 
2186 static int IsCornerExtremum(int xdir, int *contourends, BasePoint *bp, int p) {
2187  int PrevPoint = PrevOnContour(contourends, p);
2188  int NextPoint = NextOnContour(contourends, p);
2189 
2190 return xdir?
2191  ((bp[PrevPoint].x > bp[p].x && bp[NextPoint].x > bp[p].x) ||
2192  (bp[PrevPoint].x < bp[p].x && bp[NextPoint].x < bp[p].x)):
2193  ((bp[PrevPoint].y > bp[p].y && bp[NextPoint].y > bp[p].y) ||
2194  (bp[PrevPoint].y < bp[p].y && bp[NextPoint].y < bp[p].y));
2195 }
2196 
2197 static int IsAnglePoint(int *contourends, BasePoint *bp, SplinePoint *sp) {
2198  int PrevPoint, NextPoint, p=sp->ttfindex;
2199  double PrevTangent, NextTangent;
2200 
2201  if ((sp->pointtype != pt_corner) || (p == 0xffff))
2202 return 0;
2203 
2204  PrevPoint = PrevOnContour(contourends, p);
2205  NextPoint = NextOnContour(contourends, p);
2206  PrevTangent = atan2(bp[p].y - bp[PrevPoint].y, bp[p].x - bp[PrevPoint].x);
2207  NextTangent = atan2(bp[NextPoint].y - bp[p].y, bp[NextPoint].x - bp[p].x);
2208 
2209 return fabs(PrevTangent - NextTangent) > 0.261;
2210 }
2211 
2212 static int IsInflectionPoint(int *contourends, BasePoint *bp, SplinePoint *sp) {
2213  double CURVATURE_THRESHOLD = 1e-9;
2214  struct spline *prev, *next;
2215  double in, out;
2216 
2217  if (IsAnglePoint(contourends, bp, sp))
2218 return 0;
2219 
2220  /* point of a single-point contour can't be an inflection point. */
2221  if (sp->prev != NULL && sp->prev->from != NULL && sp->prev->from == sp)
2222 return 0;
2223 
2224  prev = sp->prev;
2225  in = 0;
2226  while (prev != NULL && fabs(in) < CURVATURE_THRESHOLD) {
2227  in = SplineCurvature(prev, 1);
2228  if (fabs(in) < CURVATURE_THRESHOLD) in = SplineCurvature(prev, 0);
2229  if (fabs(in) < CURVATURE_THRESHOLD) prev = prev->from->prev;
2230  if ((prev != NULL && IsAnglePoint(contourends, bp, prev->to)) || (prev == sp->prev))
2231  break;
2232  }
2233 
2234  next = sp->next;
2235  out = 0;
2236  while (next != NULL && fabs(out) < CURVATURE_THRESHOLD) {
2237  out = SplineCurvature(next, 0);
2238  if (fabs(out) < CURVATURE_THRESHOLD) out = SplineCurvature(next, 1);
2239  if (fabs(out) < CURVATURE_THRESHOLD) next = next->to->next;
2240  if ((next != NULL && IsAnglePoint(contourends, bp, next->from)) || (next == sp->next))
2241  break;
2242  }
2243 
2244  if (in==0 || out==0 || (prev != sp->prev && next != sp->next))
2245 return 0;
2246 
2247  in/=fabs(in);
2248  out/=fabs(out);
2249 
2250 return (in*out < 0);
2251 }
2252 
2253 /******************************************************************************
2254  *
2255  * I found it easier to write an iterator that calls given function for each
2256  * point worth instructing than repeating the same loops all the time.
2257  *
2258  * The control points are not skipped, but runmes often eliminate them as
2259  * instructing them seems to cause more damages than profits. They are included
2260  * here because edge optimizer cam be simpler and work more reliably then.
2261  *
2262  * The contour_direction option is for blues - snapping internal contour to a
2263  * blue zone is plain wrong, unless there is a stem hint tat don't fit to any
2264  * other blue zone.
2265  *
2266  ******************************************************************************/
2267 #define EXTERNAL_CONTOURS 0
2268 #define ALL_CONTOURS 1
2269 #define INTERNAL_CONTOURS 2
2270 static void RunOnPoints(InstrCt *ct, int contour_direction,
2271  void (*runme)(int p, SplinePoint *sp, InstrCt *ct))
2272 {
2273  SplineSet *ss = ct->ss;
2274  SplinePoint *sp;
2275  uint8 *done;
2276  int c, p;
2277 
2278  done = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
2279 
2280  for ( c=0; ss!=NULL; ss=ss->next, ++c ) {
2281  ct->cdir = ct->clockwise[c];
2282 
2283  if (((contour_direction == EXTERNAL_CONTOURS) && !ct->cdir) ||
2284  ((contour_direction == INTERNAL_CONTOURS) && ct->cdir)) continue;
2285 
2286  for ( sp=ss->first; ; ) {
2287  if (sp->ttfindex != 0xffff) {
2288  if (!sp->noprevcp &&
2289  !done[p = PrevOnContour(ct->contourends, sp->ttfindex)])
2290  {
2291  runme(p, sp, ct);
2292  done[p] = true;
2293  }
2294 
2295  if (!done[p = sp->ttfindex]) {
2296  runme(p, sp, ct);
2297  done[p] = true;
2298  }
2299 
2300  if (!sp->nonextcp && !done[p = sp->nextcpindex])
2301  {
2302  runme(p, sp, ct);
2303  done[p] = true;
2304  }
2305  }
2306  else if (!sp->nonextcp) {
2307  if (!done[p = PrevOnContour(ct->contourends, sp->nextcpindex)]) {
2308  runme(p, sp, ct);
2309  done[p] = true;
2310  }
2311 
2312  if (!done[p = sp->nextcpindex]) {
2313  runme(p, sp, ct);
2314  done[p] = true;
2315  }
2316  }
2317 
2318  if ( sp->next==NULL ) break;
2319  sp = sp->next->to;
2320  if ( sp==ss->first ) break;
2321  }
2322  }
2323 
2324  free(done);
2325 }
2326 
2327 /******************************************************************************
2328  *
2329  * Hinting is mostly aligning 'edges' (in FreeType's sense). Each stem hint
2330  * consists of two edges (or one, for ghost hints). And each blue zone can be
2331  * represented as an edge with extended fudge (overshoot).
2332  *
2333  * Hinting a stem edge is broken in two steps. First: init_stem_edge() seeks for
2334  * points to snap and chooses one that will be used as a reference point - it
2335  * should be then instructed elsewhere (a general method of edge positioning).
2336  * Old init_edge() is still used instead for blue zones and strong points.
2337  * Finally, finish_edge() instructs the rest of points found with given command,
2338  * using instructpoints(). It normally optimizes an edge before instructing,
2339  * but not in presence of diagonal hints.
2340  *
2341  * The contour_direction option of init_edge() is for hinting blues - snapping
2342  * internal contour to a bluezone seems just plainly wrong.
2343  *
2344  ******************************************************************************/
2345 
2346 /* The following operations have been separated from search_edge(), */
2347 /* because sometimes it is important to be able to determine, if the */
2348 /* given point is about to be gridfitted or interpolated */
2349 static int value_point(InstrCt *ct, int p, SplinePoint *sp, real fudge) {
2350  int score = 0;
2351  int EM = ct->gic->sf->ascent + ct->gic->sf->descent;
2352  uint8 touchflag = ct->xdir?tf_x:tf_y;
2353 
2354  if (IsCornerExtremum(ct->xdir, ct->contourends, ct->bp, p) ||
2355  IsExtremum(ct->xdir, p, sp))
2356  score+=4;
2357 
2358  if (same_angle(ct->contourends, ct->bp, p, ct->xdir?0.5*M_PI:0.0))
2359  score++;
2360 
2361  if (p == sp->ttfindex && IsAnglePoint(ct->contourends, ct->bp, sp))
2362  score++;
2363 
2364  if (interpolate_more_strong && (fudge > (EM/EDGE_FUZZ+0.0001)))
2365  if (IsExtremum(!ct->xdir, p, sp))
2366  score++;
2367 
2368  if (IsInflectionPoint(ct->contourends, ct->bp, sp))
2369  score++;
2370 
2371  if (score && ct->gd->points[p].sp != NULL) /* oncurve */
2372  score+=2;
2373 
2374  if (!score)
2375 return( 0 );
2376 
2377  if (ct->diagstems != NULL && ct->diagpts[p].count) score+=9;
2378  if (ct->touched[p] & touchflag) score+=26;
2379 return( score );
2380 }
2381 
2382 /* search for points to be snapped to an edge - to be used in RunOnPoints() */
2383 static void search_edge(int p, SplinePoint *sp, InstrCt *ct) {
2384  int tmp, score;
2385  real fudge = ct->gic->fudge;
2386  uint8 touchflag = ct->xdir?tf_x:tf_y;
2387  real refcoord, coord = ct->xdir?ct->bp[p].x:ct->bp[p].y;
2388 
2389  if (fabs(coord - ct->edge.base) <= fudge)
2390  {
2391  score = value_point(ct, p, sp, ct->gic->fudge);
2392  if (!score)
2393  return;
2394  else if (ct->edge.refpt == -1) {
2395  ct->edge.refpt = p;
2396  ct->edge.refscore = score;
2397  return;
2398  }
2399 
2400  refcoord = ct->xdir?ct->bp[ct->edge.refpt].x:ct->bp[ct->edge.refpt].y;
2401 
2402  if ((score > ct->edge.refscore) ||
2403  (score == ct->edge.refscore &&
2404  fabs(coord - ct->edge.base) < fabs(refcoord - ct->edge.base)))
2405  {
2406  tmp = ct->edge.refpt;
2407  ct->edge.refpt = p;
2408  ct->edge.refscore = score;
2409  p = tmp;
2410  }
2411 
2412  if ((p!=-1) && !((ct->touched[p] | ct->affected[p]) & touchflag)) {
2413  ct->edge.othercnt++;
2414 
2415  if (ct->edge.othercnt==1) ct->edge.others=(int *)calloc(1, sizeof(int));
2416  else ct->edge.others=(int *)realloc(ct->edge.others, ct->edge.othercnt*sizeof(int));
2417 
2418  ct->edge.others[ct->edge.othercnt-1] = p;
2419  }
2420  }
2421 }
2422 
2423 static int StemPreferredForPoint(PointData *pd, StemData *stem,int is_next ) {
2424  StemData **stems;
2425  BasePoint bp;
2426  real off, bestoff;
2427  int i, is_l, best=0, *stemcnt;
2428 
2429  stems = ( is_next ) ? pd->nextstems : pd->prevstems;
2430  stemcnt = ( is_next) ? &pd->nextcnt : &pd->prevcnt;
2431 
2432  bestoff = 1e4;
2433  for ( i=0; i<*stemcnt; i++ ) {
2434  /* Ghost hints are always assigned to both sides of a point, no matter
2435  * what the next/previous spline direction is. So we need an additional
2436  * check for stem unit parallelity */
2437  if (stems[i]->toobig > stem->toobig ||
2438  stems[i]->unit.x != stem->unit.x || stems[i]->unit.y != stem->unit.y)
2439  continue;
2440  is_l = is_next ? pd->next_is_l[i] : pd->prev_is_l[i];
2441  bp = is_l ? stems[i]->left : stems[i]->right;
2442  off = fabs(( pd->base.x - bp.x )*stem->l_to_r.x +
2443  ( pd->base.y - bp.y )*stem->l_to_r.y );
2444  if (off < bestoff || (RealNear(off, bestoff) && stems[i] == stem)) {
2445  best = i;
2446  bestoff = off;
2447  }
2448  }
2449  if (best < *stemcnt && stem == stems[best])
2450  return( best );
2451 
2452  return( -1 );
2453 }
2454 
2455 static int has_valid_dstem( PointData *pd,int next ) {
2456  int i, cnt;
2457  StemData *test;
2458 
2459  cnt = next ? pd->nextcnt : pd->prevcnt;
2460  for ( i=0; i<cnt; i++ ) {
2461  test = next ? pd->nextstems[i] : pd->prevstems[i];
2462  if ( !test->toobig && test->lpcnt > 1 && test->rpcnt > 1 &&
2463  fabs( test->unit.x ) > .05 && fabs( test->unit.y ) > .05 )
2464  return( i );
2465  }
2466  return( -1 );
2467 }
2468 
2469 /* init_stem_edge(): Initialize the InstrCt for instructing given edge.
2470  *
2471  * Finds points that should be snapped to this hint's given edge.
2472  * It will return two types of points: a 'chosen one' ct->edge.refpt, that
2473  * should be used as a reference for this hint, and ct->edge.others that should
2474  * be positioned after ct.refpt with, for example, SHP.
2475  *
2476  * assign_points_to_edge() is a helper function, only to use from init_stem_edge().
2477  */
2478 static void assign_points_to_edge(InstrCt *ct, StemData *stem, int is_l, int *refidx) {
2479  int i, previdx, nextidx, test_l, dint_inner = false, flag;
2480  PointData *pd;
2481 
2482  flag = RealNear( stem->unit.y,1 ) ? tf_x : tf_y;
2483 
2484  for ( i=0; i<ct->gd->realcnt; i++ ) {
2485  pd = &ct->gd->points[i];
2486  previdx = StemPreferredForPoint( pd,stem,false );
2487  nextidx = StemPreferredForPoint( pd,stem,true );
2488  if (!pd->ticked && (previdx != -1 || nextidx != -1)) {
2489  pd->ticked = true;
2490  /* Don't attempt to position inner points at diagonal intersections:
2491  * our diagonal stem hinter will handle them better */
2492  if ( ct->diagcnt > 0 && (
2493  ( stem->unit.y == 1 && pd->x_corner == 2 ) ||
2494  ( stem->unit.x == 1 && pd->y_corner == 2 ))) {
2495 
2496  dint_inner= has_valid_dstem( pd,true ) != -1 &&
2497  has_valid_dstem( pd,false ) != -1;
2498  }
2499  test_l = (nextidx != -1) ?
2500  pd->next_is_l[nextidx] : pd->prev_is_l[previdx];
2501  if (test_l == is_l && !dint_inner &&
2502  !(ct->touched[pd->ttfindex] & flag) && !(ct->affected[pd->ttfindex] & flag)) {
2503  ct->edge.others = (int *)realloc(
2504  ct->edge.others, (ct->edge.othercnt+1)*sizeof(int));
2505  ct->edge.others[ct->edge.othercnt++] = pd->ttfindex;
2506  if ( *refidx == -1 ) *refidx = pd->ttfindex;
2507  }
2508  }
2509  }
2510 }
2511 
2512 static void init_stem_edge(InstrCt *ct, StemData *stem, int is_l) {
2513  real left, right, base;
2514  struct dependent_stem *slave;
2515  PointData *rpd = NULL;
2516  int i, *refidx = NULL;
2517 
2518  left = ( stem->unit.x == 0 ) ? stem->left.x : stem->left.y;
2519  right = ( stem->unit.x == 0 ) ? stem->right.x : stem->right.y;
2520  base = ( is_l ) ? left : right;
2521 
2522  ct->edge.base = base;
2523  ct->edge.refpt = -1;
2524  ct->edge.refscore = 0;
2525  ct->edge.othercnt = 0;
2526  ct->edge.others = NULL;
2527 
2528  refidx = ( is_l ) ? &stem->leftidx : &stem->rightidx;
2529  if ( *refidx != -1 )
2530  rpd = &ct->gd->points[*refidx];
2531 
2532  /* Don't attempt to position inner points at diagonal intersections:
2533  * our diagonal stem hinter will handle them better */
2534  if ( rpd != NULL && ct->diagcnt > 0 && (
2535  ( stem->unit.y == 1 && rpd->x_corner == 2 ) ||
2536  ( stem->unit.x == 1 && rpd->y_corner == 2 )) &&
2537  has_valid_dstem( rpd,true ) != -1 && has_valid_dstem( rpd,false ) != -1 )
2538  *refidx = -1;
2539 
2540  for ( i=0; i<ct->gd->realcnt; i++ )
2541  ct->gd->points[i].ticked = false;
2542  assign_points_to_edge(ct, stem, is_l, refidx);
2543 
2544  for ( i=0; i<stem->dep_cnt; i++ ) {
2545  slave = &stem->dependent[i];
2546  if (slave->dep_type == 'a' &&
2547  ((is_l && slave->lbase) || (!is_l && !slave->lbase))) {
2548 
2549  if ( is_l ) slave->stem->leftidx = *refidx;
2550  else slave->stem->rightidx = *refidx;
2551  assign_points_to_edge(ct, slave->stem, is_l, refidx);
2552  }
2553  }
2554  ct->edge.refpt = *refidx;
2555 }
2556 
2557 /* Initialize the InstrCt for instructing given edge. */
2558 static void init_edge(InstrCt *ct, real base, int contour_direction) {
2559  ct->edge.base = base;
2560  ct->edge.refpt = -1;
2561  ct->edge.refscore = 0;
2562  ct->edge.othercnt = 0;
2563  ct->edge.others = NULL;
2564 
2565  RunOnPoints(ct, contour_direction, &search_edge);
2566 }
2567 
2568 /* Apparatus for edge hinting optimization. For given 'others' in ct,
2569  * it detects 'segments' (in FreeType's sense) and leaves only one point per
2570  * segment. A segment to which refpt belong is completely removed (refpt is
2571  * enough).
2572  *
2573  * optimize_edge() is the right high-level function to call with instructing
2574  * context (an edge must be previously initialized with init_edge). It calls
2575  * optimize_segment() internally - a function that is otherwise unsafe.
2576  *
2577  * optimize_blue() is even higher-level function to call before optimize_edge
2578  * if init_edge() was used to collect points in a blue zone (or other narrow
2579  * zone).
2580  *
2581  * Optimizers keep points used by diagonal hinter.
2582  *
2583  * optimize_strongpts() is used instead of two routines above when hinting
2584  * inter-stem zones (see interpolate_strong option). It's invoked after
2585  * instructing diagonal stems.
2586  */
2587 
2588 /* To be used with qsort() - sorts integer array in ascending order. */
2589 static int sortbynum(const void *a, const void *b) {
2590  return *(int *)a > *(int *)b;
2591 }
2592 
2593 /* Find element's index within an array - return -1 if element not found. */
2594 static int findoffs(const int *elems, int elemcnt, int val) {
2595  int i;
2596  for (i=0; i<elemcnt; i++) if (elems[i]==val) return i;
2597  return -1;
2598 }
2599 
2600 /* In given ct, others[segstart...segend] form a continuous segment on an edge
2601  * parallel to one of coordinate axes. If there are no diagonal hints, we can
2602  * instruct just one point of a segment, preferring refpt if included, and
2603  * preferring on-curve points ovef off-curve. Otherwise we must instruct all
2604  * points used by diagonal hinter along with refpt if included. We mark points
2605  * that are not to be instructed as 'affected'.
2606  */
2607 static void optimize_segment(int segstart, int segend, InstrCt *ct) {
2608  int i, local_refpt=-1;
2609  int *others = ct->edge.others;
2610  int touchflag = (ct->xdir)?tf_x:tf_y;
2611  int ondiags = 0;
2612 
2613  if (segstart==segend)
2614 return;
2615 
2616  /* purely for aesthetic reasons - can be safely removed. */
2617  qsort(others+segstart, segend+1-segstart, sizeof(int), sortbynum);
2618 
2619  /* are there any to be used with dstems? */
2620  if (ct->diagstems)
2621  for (i=segstart; !ondiags && i<=segend; i++)
2622  ondiags = ct->diagpts[others[i]].count;
2623 
2624  if (ondiags) {
2625  for (i=segstart; i<=segend; i++)
2626  ct->affected[others[i]] |= ct->diagpts[others[i]].count?0:touchflag;
2627  }
2628  else {
2629  for (i=segstart; i<=segend && ct->gd->points[others[i]].sp == NULL; i++);
2630  if (i<=segend) local_refpt = others[i];
2631 
2632  if (findoffs(others+segstart, segend+1-segstart, ct->edge.refpt) != -1)
2633  local_refpt = ct->edge.refpt;
2634 
2635  if (local_refpt==-1) local_refpt = others[segstart];
2636 
2637  for (i=segstart; i<=segend; i++)
2638  ct->affected[others[i]] |= local_refpt==others[i]?0:touchflag;
2639  }
2640 }
2641 
2642 /* Subdivide an edge into segments and optimize segments separately.
2643  * A segment consists oh a point, his neighbours, their neighbours...
2644  */
2645 static void optimize_edge(InstrCt *ct) {
2646  int i, p, segstart, next;
2647  int refpt = ct->edge.refpt;
2648  int *others = ct->edge.others;
2649  int othercnt = ct->edge.othercnt;
2650  int touchflag = (ct->xdir)?tf_x:tf_y;
2651 
2652  if (othercnt == 0)
2653 return;
2654 
2655  /* add edge.refpt to edge.others */
2656  ct->edge.othercnt = ++othercnt;
2657  ct->edge.others = others = (int *)realloc(others, othercnt*sizeof(int));
2658  others[othercnt-1]=refpt;
2659 
2660  next = 0;
2661  while (next < othercnt) {
2662  p = others[segstart = next++];
2663 
2664  while((next < othercnt) && (i = findoffs(others+next, othercnt-next,
2665  NextOnContour(ct->contourends, p))) != -1) {
2666  p = others[i+=next];
2667  others[i] = others[next];
2668  others[next++] = p;
2669  }
2670 
2671  p=others[segstart];
2672 
2673  while((next < othercnt) && (i = findoffs(others+next, othercnt-next,
2674  PrevOnContour(ct->contourends, p))) != -1) {
2675  p = others[i+=next];
2676  others[i] = others[next];
2677  others[next++] = p;
2678  }
2679 
2680  optimize_segment(segstart, next-1, ct);
2681  }
2682 
2683  for (i=next=0; i<othercnt; i++)
2684  if (!(ct->affected[others[i]] & touchflag) && (others[i] != refpt))
2685  others[next++] = others[i];
2686 
2687  if ((ct->edge.othercnt = next) == 0) {
2688  free(others);
2689  ct->edge.others = NULL;
2690  }
2691  else /* purely for aesthetic reasons - could be safely removed. */
2692  qsort(others, ct->edge.othercnt, sizeof(int), sortbynum);
2693 }
2694 
2695 /* For any given point on edge, if there exists a path to other point snapped
2696  * or to-be-snapped in that zone, such that any points on this path are within
2697  * that zone, then this given point may be optimized out.
2698  */
2699 static void optimize_blue(InstrCt *ct) {
2700  int i, j, curr;
2701  int *others = ct->edge.others;
2702  int othercnt = ct->edge.othercnt;
2703  int touchflag = (ct->xdir)?tf_x:tf_y;
2704  int *contourends = ct->contourends;
2705  uint8 *touched = ct->touched;
2706  uint8 *affected = ct->affected;
2707  uint8 *tosnap;
2708 
2709  if (othercnt == 0)
2710 return;
2711 
2712  tosnap = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
2713 
2714  for(i=0; i<ct->edge.othercnt; i++)
2715  {
2716  if (ct->diagpts && ct->diagpts[others[i]].count) continue;
2717 
2718  /* check path forward */
2719  curr=NextOnContour(contourends, others[i]);
2720  while(curr!=others[i]) {
2721  double coord = (ct->xdir) ? ct->bp[curr].x : ct->bp[curr].y;
2722  if (fabs(ct->edge.base - coord) > ct->gic->fudge) break;
2723  if ((touched[curr] | affected[curr]) & touchflag || tosnap[curr]) {
2724  affected[others[i]] |= touchflag;
2725  break;
2726  }
2727  curr=NextOnContour(contourends, curr);
2728  }
2729 
2730  if (affected[others[i]] & touchflag) continue;
2731 
2732  /* check path backward */
2733  curr=PrevOnContour(contourends, others[i]);
2734  while(curr!=others[i]) {
2735  double coord = (ct->xdir) ? ct->bp[curr].x : ct->bp[curr].y;
2736  if (fabs(ct->edge.base - coord) > ct->gic->fudge) break;
2737  if ((touched[curr] | affected[curr]) & touchflag || tosnap[curr]) {
2738  affected[others[i]] |= touchflag;
2739  break;
2740  }
2741  curr=PrevOnContour(contourends, curr);
2742  }
2743 
2744  if (!(affected[others[i]] & touchflag)) tosnap[others[i]] = 1;
2745  }
2746 
2747  free(tosnap);
2748 
2749  /* remove optimized-out points from list to be instructed. */
2750  for(i=0; i<ct->edge.othercnt; i++)
2751  if (affected[others[i]]) {
2752  ct->edge.othercnt--;
2753  for(j=i; j<ct->edge.othercnt; j++) others[j] = others[j+1];
2754  i--;
2755  }
2756 }
2757 
2758 /* For any strong point, check whether it's position can rely on other
2759  * points (if so, we don't have to instruct it explicitly).
2760  * This optimization is two-pass. 'Obvious' Off-curve points are sweeped
2761  * first. Some remaining unneeded points (off- and on-curve) may then be
2762  * optimized out in second pass.
2763  *
2764  * TODO! This optimizer could be even more aggressive - it currently
2765  * skips some features too small or unexposed to benefit from hinting.
2766  */
2767 static void optimize_strongpts_step1(InstrCt *ct);
2768 static void optimize_strongpts_step2(InstrCt *ct);
2769 
2770 static void optimize_strongpts(InstrCt *ct) {
2773 }
2774 
2776  int i, j;
2777  int *others = ct->edge.others;
2778  int othercnt = ct->edge.othercnt;
2779  int *contourends = ct->contourends;
2780  uint8 *tocull, *tocheck;
2781 
2782  if (othercnt == 0)
2783 return;
2784 
2785  tocull = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
2786  tocheck = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
2787  for(i=0; i<ct->edge.othercnt; i++) tocheck[ct->edge.others[i]] = 1;
2788 
2789  /* for each point of "edge" (would be better called "zone") */
2790  for(i=0; i<ct->edge.othercnt; i++)
2791  {
2792  int pt = others[i];
2793  double pt_x = ct->bp[pt].x;
2794  double pt_y = ct->bp[pt].y;
2795 
2796  int pt_next = NextOnContour(contourends, pt);
2797  double pt_next_x = ct->bp[pt_next].x;
2798  double pt_next_y = ct->bp[pt_next].y;
2799 
2800  int pt_prev = PrevOnContour(contourends, pt);
2801  double pt_prev_x = ct->bp[pt_prev].x;
2802  double pt_prev_y = ct->bp[pt_prev].y;
2803 
2804  /* We sweep only off-curve points here */
2805  if (ct->gd->points[pt].sp != NULL)
2806  continue;
2807 
2808  if (IsCornerExtremum(ct->xdir, ct->contourends, ct->bp, pt))
2809  continue;
2810 
2811  /* Some off-curve points may 'belong' to extrema from other zone. */
2812 
2813  if (/*tocheck[pt_next] &&*/ (ct->gd->points[pt_next].sp != NULL) &&
2814  (pt_x == pt_next_x || pt_y == pt_next_y))
2815  tocull[pt] = 1;
2816 
2817  if (/*tocheck[pt_prev] &&*/ (ct->gd->points[pt_prev].sp != NULL) &&
2818  (pt_x == pt_prev_x || pt_y == pt_prev_y))
2819  tocull[pt] = 1;
2820  }
2821 
2822  /* remove optimized-out points from list to be instructed. */
2823  for(i=0; i<ct->edge.othercnt; i++)
2824  if (tocull[others[i]]) {
2825  ct->edge.othercnt--;
2826  for(j=i; j<ct->edge.othercnt; j++) others[j] = others[j+1];
2827  i--;
2828  }
2829 
2830  free(tocull);
2831  free(tocheck);
2832 }
2833 
2835  int pass, i, j, forward;
2836  int next_closed, prev_closed;
2837  int next_pt_max, next_pt_min, prev_pt_max, prev_pt_min;
2838  int next_coord_max, next_coord_min, prev_coord_max, prev_coord_min;
2839  int *others = ct->edge.others;
2840  int othercnt = ct->edge.othercnt;
2841  int touchflag = (ct->xdir)?tf_x:tf_y;
2842  int *contourends = ct->contourends;
2843  uint8 *touched = ct->touched;
2844  uint8 *affected = ct->affected;
2845  uint8 *toinstr, *tocull, *tocheck;
2846 
2847  if (othercnt == 0)
2848 return;
2849 
2850  toinstr = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
2851  tocull = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
2852  tocheck = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
2853  for(i=0; i<ct->edge.othercnt; i++) tocheck[ct->edge.others[i]] = 1;
2854 
2855  /* two passes... */
2856  for(pass=0; pass<2; pass++)
2857  {
2858  /* ...for each point of "edge" (would be better called "zone" here) */
2859  for(i=0; i<ct->edge.othercnt; i++)
2860  {
2861  int pt = others[i];
2862  double pt_coord = (ct->xdir) ? ct->bp[pt].x : ct->bp[pt].y;
2863 
2864  /* In first pass, we sweep only off-curve points */
2865  if ((pass==0) && (ct->gd->points[pt].sp != NULL))
2866  continue;
2867 
2868  if (tocull[pt] || toinstr[pt])
2869  continue;
2870 
2871  /* check path backward and forward */
2872  for (forward=0; forward<2; forward++)
2873  {
2874  int closed = 0;
2875  int pt_max = pt, pt_min = pt;
2876  double coord_max = pt_coord, coord_min = pt_coord;
2877  int curr = forward ? NextOnContour(contourends, pt):
2878  PrevOnContour(contourends, pt);
2879 
2880  while(curr!=pt)
2881  {
2882  double coord = (ct->xdir) ? ct->bp[curr].x : ct->bp[curr].y;
2883 
2884  if (fabs(ct->edge.base - coord) > ct->gic->fudge)
2885  break;
2886 
2887  if ((touched[curr] | affected[curr]) & touchflag || tocheck[curr])
2888  {
2889  if (coord > coord_max) { coord_max = coord; pt_max = curr; }
2890  else if ((coord == coord_max) && (curr < pt_max)) pt_max = curr;
2891 
2892  if (coord < coord_min) { coord_min = coord; pt_min = curr; }
2893  else if ((coord == coord_min) && (curr < pt_min)) pt_min = curr;
2894 
2895  closed = 1;
2896  }
2897 
2898  if ((touched[curr] | affected[curr]) & touchflag || toinstr[curr])
2899  break;
2900 
2901  curr = forward ? NextOnContour(contourends, curr):
2902  PrevOnContour(contourends, curr);
2903  }
2904 
2905  if (forward) {
2906  next_closed = closed;
2907  next_pt_max = pt_max;
2908  next_pt_min = pt_min;
2909  next_coord_max = coord_max;
2910  next_coord_min = coord_min;
2911  }
2912  else {
2913  prev_closed = closed;
2914  prev_pt_max = pt_max;
2915  prev_pt_min = pt_min;
2916  prev_coord_max = coord_max;
2917  prev_coord_min = coord_min;
2918  }
2919  }
2920 
2921  if (prev_closed && next_closed && (
2922  (prev_coord_max >= pt_coord && pt != prev_pt_max &&
2923  next_coord_min <= pt_coord && pt != next_pt_min) ||
2924  (prev_coord_min <= pt_coord && pt != prev_pt_min &&
2925  next_coord_max >= pt_coord && pt != next_pt_max)))
2926  tocull[pt] = 1;
2927  else
2928  toinstr[pt] = 1;
2929  }
2930  }
2931 
2932  /* remove optimized-out points from list to be instructed. */
2933  for(i=0; i<ct->edge.othercnt; i++)
2934  if (tocull[others[i]]) {
2935  ct->edge.othercnt--;
2936  for(j=i; j<ct->edge.othercnt; j++) others[j] = others[j+1];
2937  i--;
2938  }
2939 
2940  free(tocheck);
2941  free(toinstr);
2942  free(tocull);
2943 }
2944 
2945 /* Finish instructing the edge. Try to hint only those points on edge that are
2946  * necessary - IUP should do the rest.
2947  */
2948 static void finish_edge(InstrCt *ct, uint8 command) {
2949  int i;
2950 
2951  optimize_edge(ct);
2952  if (ct->edge.othercnt==0)
2953 return;
2954 
2955  ct->pt=instructpoints(ct->pt, ct->edge.othercnt, ct->edge.others, command);
2956  for(i=0; i<ct->edge.othercnt; i++)
2957  ct->touched[ct->edge.others[i]] |= (ct->xdir?tf_x:tf_y);
2958 
2959  free(ct->edge.others);
2960  ct->edge.others=NULL;
2961  ct->edge.othercnt = 0;
2962 }
2963 
2964 /******************************************************************************
2965  *
2966  * Routines for hinting single stems.
2967  *
2968  ******************************************************************************/
2969 
2970 /* Each stem hint has 'ldone' and 'rdone' flag, indicating whether 'left'
2971  * or 'right' edge is hinted or not. This functions marks as done all edges at
2972  * specified coordinate, starting from given hint (hints sometimes share edges).
2973  */
2974 static void mark_startenddones(StemData *stem, int is_l ) {
2975  struct dependent_stem *slave;
2976  int i;
2977  uint8 *done;
2978 
2979  done = is_l ? &stem->ldone : &stem->rdone;
2980  *done = true;
2981  for (i=0; i<stem->dep_cnt; i++) {
2982  slave = &stem->dependent[i];
2983  if ( slave->dep_type == 'a' && slave->lbase == is_l ) {
2984  done = is_l ? &slave->stem->ldone : &slave->stem->rdone;
2985  *done = true;
2986  }
2987  }
2988 }
2989 
2990 static void build_cvt_stem(InstrCt *ct, real width, StdStem *cvt_stem) {
2991  int i, width_parent, width_me;
2992  int EM = ct->gic->sf->ascent + ct->gic->sf->descent;
2993 
2994  cvt_stem->width = (int)rint(fabs(width));
2995  cvt_stem->stopat = 32767;
2996  cvt_stem->snapto =
2997  CVTSeekStem(ct->xdir, ct->gic, width, false);
2998 
2999  for (i=7; i<32768; i++) {
3000  width_parent = compute_stem_width(ct->xdir, cvt_stem->snapto, EM, i);
3001  width_me = compute_stem_width(ct->xdir, cvt_stem, EM, i);
3002 
3003  if (width_parent != width_me) {
3004  cvt_stem->stopat = i;
3005  break;
3006  }
3007  }
3008 }
3009 
3010 /* This function has been separated from finish_stem(), because sometimes
3011  * it is necessary to maintain the distance between two points (usually on
3012  * opposite stem edges) without instructing the whole stem. Currently we use this
3013  * to achieve proper positioning of the left edge of a vertical stem in antialiased
3014  * mode, if instructing this stem has to be started from the right edge
3015  */
3016 static void maintain_black_dist(InstrCt *ct, real width, int refpt, int chg_rp0) {
3017  int callargs[5];
3018  StdStem *StdW = ct->xdir?&(ct->gic->stdvw):&(ct->gic->stdhw);
3019  StdStem *ClosestStem;
3020  StdStem cvt_stem;
3021 
3022  ClosestStem = CVTSeekStem(ct->xdir, ct->gic, width, true);
3023 
3024  if (ClosestStem != NULL) {
3025  ct->pt = push2nums(ct->pt, refpt, ClosestStem->cvtindex);
3026 
3027  if (ct->gic->cvt_done && ct->gic->fpgm_done && ct->gic->prep_done)
3028  *(ct->pt)++ = chg_rp0?MIRP_rp0_min_black:MIRP_min_black;
3029  else *(ct->pt)++ = chg_rp0?MIRP_min_rnd_black:MIRP_rp0_min_rnd_black;
3030  }
3031  else {
3032  if (ct->gic->cvt_done && ct->gic->fpgm_done && ct->gic->prep_done &&
3033  StdW->width!=-1)
3034  {
3035  build_cvt_stem(ct, width, &cvt_stem);
3036 
3037  callargs[0] = ct->edge.refpt;
3038  callargs[1] = cvt_stem.snapto->cvtindex;
3039  callargs[2] = chg_rp0?1:0;
3040  callargs[3] = cvt_stem.stopat;
3041  callargs[4] = 4;
3042  ct->pt = pushnums(ct->pt, 5, callargs);
3043  *(ct->pt)++ = CALL;
3044  }
3045  else {
3046  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3047  *(ct->pt)++ = chg_rp0?MDRP_rp0_min_rnd_black:MDRP_min_rnd_black;
3048  }
3049  }
3050 }
3051 
3052 /* Given the refpt for one of this hint's edges is already positioned, this
3053  * function aligns 'others' (SHP with given shp_rp) for this edge and positions
3054  * the second edge, optionally setting its refpt as rp0. It frees edge.others
3055  * and sets edge.othercnt to zero, but it leaves edge.refpt set to last
3056  * instructed edge.
3057  */
3058 #define use_rp1 (true)
3059 #define use_rp2 (false)
3060 #define set_new_rp0 (true)
3061 #define keep_old_rp0 (false)
3062 static void finish_stem(StemData *stem, int shp_rp1, int chg_rp0, InstrCt *ct)
3063 {
3064  int is_l, basedone, oppdone, reverse;
3065  real hleft, hright, width;
3066 
3067  if (stem == NULL)
3068  return;
3069  hleft = ((real *) &stem->left.x)[!ct->xdir];
3070  hright= ((real *) &stem->right.x)[!ct->xdir];
3071 
3072  is_l = (fabs(hleft - ct->edge.base) < fabs(hright - ct->edge.base));
3073  basedone = ( is_l && stem->ldone ) || ( !is_l && stem->rdone );
3074  oppdone = ( is_l && stem->rdone ) || ( !is_l && stem->ldone );
3075  reverse = ( ct->xdir && !is_l && !stem->ldone && !stem->ghost );
3076  width = stem->width;
3077 
3078  if ( !reverse && !basedone ) {
3079  ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
3080  finish_edge(ct, shp_rp1?SHP_rp1:SHP_rp2);
3082  }
3083 
3084  if (oppdone || (stem->ghost && ((stem->width==20) || (stem->width==21)))) {
3085  stem->ldone = stem->rdone = 1;
3086  return;
3087  }
3088 
3089  init_stem_edge(ct, stem, !is_l);
3090  if (ct->edge.refpt == -1) {
3091  /* We have skipped the right edge to start instructing this stem from
3092  * left. But its left edge appears to have no points to be instructed.
3093  * So return to the right edge and instruct it before exiting */
3094  if ( reverse && !basedone ) {
3095  init_stem_edge(ct, stem, is_l);
3096  ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
3097  finish_edge(ct, shp_rp1?SHP_rp1:SHP_rp2);
3099  }
3100  return;
3101  }
3102  maintain_black_dist(ct, width, ct->edge.refpt, chg_rp0);
3103 
3104  if ( reverse ) {
3105  is_l = !is_l;
3106  ct->rp0 = ct->edge.refpt;
3107  ct->pt = pushpoint(ct->pt, ct->rp0);
3108  *(ct->pt)++ = MDAP_rnd;
3109  ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
3110  finish_edge(ct, SHP_rp1);
3112  if ( !stem->rdone ) {
3113  init_stem_edge(ct, stem, false);
3114  if (ct->edge.refpt == -1)
3115  return;
3116  maintain_black_dist(ct, width, ct->edge.refpt, chg_rp0);
3117  }
3118  }
3119 
3120  if (chg_rp0) ct->rp0 = ct->edge.refpt;
3121  ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
3122  finish_edge(ct, SHP_rp2);
3124 }
3125 
3127  Spline *s;
3128  PointData *pd, *cpd;
3129  int cpidx;
3130 
3131  s = next ? opd->sp->next : opd->sp->prev;
3132  pd = next ? &ct->gd->points[s->to->ptindex] : &ct->gd->points[s->from->ptindex];
3133  while (IsStemAssignedToPoint(pd, target, !next) == -1) {
3134  if (pd->ttfindex < ct->gd->realcnt &&
3135  value_point(ct, pd->ttfindex, pd->sp, ct->gd->emsize))
3136  ct->affected[pd->ttfindex] |= ct->xdir?tf_x:tf_y;
3137 
3138  if (!pd->sp->noprevcp) {
3139  cpidx = pd->sp->prev->from->nextcpindex;
3140  cpd = &ct->gd->points[cpidx];
3141  if (value_point(ct, cpd->ttfindex, pd->sp, ct->gd->emsize))
3142  ct->affected[cpd->ttfindex] |= ct->xdir?tf_x:tf_y;
3143  }
3144  if (!pd->sp->nonextcp) {
3145  cpidx = pd->sp->nextcpindex;
3146  cpd = &ct->gd->points[cpidx];
3147  if (value_point(ct, cpd->ttfindex, pd->sp, ct->gd->emsize))
3148  ct->affected[cpd->ttfindex] |= ct->xdir?tf_x:tf_y;
3149  }
3150  s = next ? pd->sp->next : pd->sp->prev;
3151  pd = next ? &ct->gd->points[s->to->ptindex] : &ct->gd->points[s->from->ptindex];
3152  if ( pd == opd ) {
3153  IError( "The ball terminal with a key point at %.3f,%.3f\n"
3154  "appears to be incorrectly linked to the %s stem\n"
3155  "<%.3f, %.3f>",
3156  pd->base.x,pd->base.y,
3157  ct->xdir?"vertical":"horizontal",
3158  ct->xdir?target->left.x:target->right.y,target->width );
3159  break;
3160  }
3161  }
3162 }
3163 
3164 static void finish_serif(StemData *slave, StemData *master, int lbase, int is_ball, InstrCt *ct)
3165 {
3166  int inner_pt, callargs[4];
3167  struct stem_chunk *chunk;
3168  PointData *opd;
3169  int i;
3170 
3171  if (slave == NULL || master == NULL)
3172 return;
3173  inner_pt = ( lbase ) ? master->rightidx : master->leftidx;
3174 
3175  init_stem_edge(ct, slave, !lbase);
3176  if (ct->edge.refpt == -1)
3177 return;
3178 
3179  if (ct->gic->fpgm_done) {
3180  callargs[0] = is_ball ? 0 : 64;
3181  callargs[1] = inner_pt;
3182  callargs[2] = ct->edge.refpt;
3183  callargs[3] = 9;
3184  ct->pt = pushnums(ct->pt, 4, callargs);
3185  *(ct->pt)++ = CALL;
3186  }
3187  else {
3188  *(ct->pt)++ = 0x7D; /* RDTG */
3189  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3190  *(ct->pt)++ = MDRP_min_rnd_black;
3191  *(ct->pt)++ = 0x18; /* RTG */
3192  }
3193 
3194  ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
3195  finish_edge(ct, SHP_rp2);
3196  mark_startenddones( slave, !lbase );
3197 
3199 return;
3200 
3201  /* Preserve points on ball terminals from being interpolated
3202  * between edges by marking them as affected */
3203  for ( i=0; i<slave->chunk_cnt; i++ ) {
3204  chunk = &slave->chunks[i];
3205  opd = lbase ? chunk->r : chunk->l;
3206 
3207  if (chunk->is_ball && opd != NULL) {
3208  mark_points_affected(ct, chunk->ball_m, opd, true);
3209  mark_points_affected(ct, chunk->ball_m, opd, false);
3210  }
3211  }
3212 }
3213 
3214 static void link_serifs_to_edge(InstrCt *ct, StemData *stem, int is_l) {
3215  int i, callargs[3];
3216  struct dependent_serif *serif;
3217 
3218  /* We use an FPGM function to set rp0, and thus the exact value
3219  * is not known at the compilation time. So it is safer to reset
3220  * ct->rp0 to -1
3221  */
3222  if ( ct->gic->fpgm_done ) {
3223  ct->rp0 = -1;
3224  callargs[0] = is_l ? stem->rightidx : stem->leftidx;
3225  callargs[1] = is_l ? stem->leftidx : stem->rightidx;
3226  callargs[2] = 10;
3227  ct->pt = pushnums(ct->pt, 3, callargs);
3228  *(ct->pt)++ = CALL;
3229  } else {
3230  init_stem_edge(ct, stem, !is_l);
3231  if ( ct->rp0 != ct->edge.refpt ) {
3232  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3233  *(ct->pt)++ = SRP0;
3234  ct->rp0 = ct->edge.refpt;
3235  }
3236  }
3237  for (i=0; i<stem->serif_cnt; i++) {
3238  serif = &stem->serifs[i];
3239  if (serif->lbase == is_l &&
3240  ((serif->is_ball && instruct_ball_terminals) ||
3241  (!serif->is_ball && instruct_serif_stems)))
3242  finish_serif( serif->stem,stem,is_l,serif->is_ball,ct );
3243  }
3244 }
3245 
3246 static void instruct_serifs(InstrCt *ct, StemData *stem) {
3247  int i, lcnt, rcnt;
3248  struct dependent_serif *serif;
3249 
3250  if ( stem->leftidx == -1 || stem->rightidx == -1 )
3251  return;
3252  lcnt = rcnt = 0;
3253  for (i=0; i<stem->serif_cnt; i++) {
3254  serif = &stem->serifs[i];
3255  if ((serif->is_ball && !instruct_ball_terminals) ||
3256  (!serif->is_ball && !instruct_serif_stems))
3257  continue;
3258  if ( serif->lbase )
3259  lcnt++;
3260  else if ( !serif->lbase )
3261  rcnt++;
3262  }
3263 
3264  if (stem->ldone && lcnt > 0)
3265  link_serifs_to_edge(ct, stem, true);
3266  if (stem->rdone && rcnt > 0)
3267  link_serifs_to_edge(ct, stem, false);
3268 }
3269 
3271  int i, j, rp, rp1, rp2, stopat, callargs[4];
3272  struct dependent_stem *slave;
3273  int w_master, w_slave;
3274  StdStem *std_master, *std_slave, norm_master, norm_slave;
3275  StdStem *StdW = ct->xdir?&(ct->gic->stdvw):&(ct->gic->stdhw);
3276 
3277  for (i=0; i<stem->dep_cnt; i++) {
3278  slave = &stem->dependent[i];
3279  if (slave->stem->master == NULL)
3280  continue;
3281 
3282  init_stem_edge(ct, slave->stem, slave->lbase);
3283  if (ct->edge.refpt == -1) continue;
3284 
3285  if (slave->dep_type == 'i' && stem->ldone && stem->rdone) {
3286  rp1 = ct->xdir ? stem->leftidx : stem->rightidx;
3287  rp2 = ct->xdir ? stem->rightidx : stem->leftidx;
3288  callargs[0] = ct->edge.refpt;
3289  callargs[1] = rp2;
3290  callargs[2] = rp1;
3291  if (ct->gic->fpgm_done) {
3292  callargs[3] = 8;
3293  ct->pt = pushpoints(ct->pt, 4, callargs);
3294  *(ct->pt)++ = CALL;
3295  } else {
3296  ct->pt = pushpoints(ct->pt, 3, callargs);
3297  *(ct->pt)++ = SRP1;
3298  *(ct->pt)++ = SRP2;
3299  *(ct->pt)++ = DUP;
3300  *(ct->pt)++ = IP;
3301  *(ct->pt)++ = MDAP_rnd;
3302  }
3303  }
3304  else if (slave->dep_type == 'm' &&
3305  ((slave->lbase && stem->ldone) || (!slave->lbase && stem->rdone))) {
3306 
3307  rp = slave->lbase ? stem->leftidx : stem->rightidx;
3308  if ( rp != ct->rp0 ) {
3309  ct->pt = pushpoint(ct->pt, rp);
3310  *(ct->pt)++ = SRP0;
3311  ct->rp0 = rp;
3312  }
3313 
3314  /* It is possible that at certain PPEMs both the master and slave stems are
3315  * regularized, say, to 1 pixel, but the difference between their positions
3316  * is rounded to 1 pixel too. Thus one stem is shifted relatively to another,
3317  * so that the overlap disappears. This looks especially odd for nesting/nested
3318  * stems. We use a special FPGM function to prevent this.
3319  */
3320  if ( ct->gic->cvt_done && ct->gic->fpgm_done && ct->gic->prep_done && StdW->width!=-1 && (
3321  ((&stem->left.x)[!ct->xdir] <= (&slave->stem->left.x)[!ct->xdir] &&
3322  ( &stem->right.x)[!ct->xdir] >= (&slave->stem->right.x)[!ct->xdir] ) ||
3323  ((&stem->left.x)[!ct->xdir] >= (&slave->stem->left.x)[!ct->xdir] &&
3324  ( &stem->right.x)[!ct->xdir] <= (&slave->stem->right.x)[!ct->xdir] ))) {
3325 
3326  std_master = CVTSeekStem(ct->xdir, ct->gic, stem->width, true);
3327  std_slave = CVTSeekStem(ct->xdir, ct->gic, slave->stem->width, true);
3328  if ( std_master == NULL ) {
3329  build_cvt_stem(ct, stem->width, &norm_master);
3330  std_master = &norm_master;
3331  }
3332  if ( std_slave == NULL ) {
3333  build_cvt_stem(ct, slave->stem->width, &norm_slave);
3334  std_slave = &norm_slave;
3335  }
3336 
3337  stopat = 32768;
3338  for (j=7; j<=stopat; j++) {
3339  w_master = compute_stem_width(ct->xdir, std_master, ct->gd->emsize, j);
3340  w_slave = compute_stem_width(ct->xdir, std_slave , ct->gd->emsize, j);
3341 
3342  if (w_master != w_slave)
3343  stopat = j;
3344  }
3345  callargs[0] = stopat;
3346  callargs[1] = ct->rp0;
3347  callargs[2] = ct->edge.refpt;
3348  callargs[3] = 14;
3349  ct->pt = pushpoints(ct->pt, 4, callargs);
3350  *(ct->pt)++ = CALL;
3351  }
3352  else {
3353  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3354  *(ct->pt)++ = DUP;
3355  *(ct->pt)++ = MDRP_rp0_rnd_white;
3356  *(ct->pt)++ = SRP1;
3357  }
3358  }
3359  else if (slave->dep_type == 'a' &&
3360  ((slave->lbase && stem->ldone) || (!slave->lbase && stem->rdone))) {
3361  if ( ct->edge.refpt != ct->rp0 ) {
3362  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3363  *(ct->pt)++ = SRP0;
3364  }
3365  }
3366  else
3367  continue;
3368 
3369  ct->rp0 = ct->edge.refpt;
3370  finish_stem(slave->stem, use_rp1, keep_old_rp0, ct);
3372  instruct_serifs(ct, slave->stem);
3373 
3374  instruct_dependent(ct, slave->stem);
3375  }
3376 }
3377 
3378 /******************************************************************************
3379  *
3380  * I decided to do snapping to blues at the very beginning of the instructing.
3381  *
3382  * Blues are processed in certain (important) order: baseline, descenders
3383  * (from deeper to shorter), ascenders (from taller to shorter).
3384  *
3385  * For each blue, one of the edges is put into CVT: lower if is't > zero,
3386  * the upper otherwise. A twilight point 0 is established at this height. All
3387  * the glyph's points decided to be worth snapping are then moved relative to
3388  * this twilight point, being subject to rounding 'down-to-int'. Space taken
3389  * is at most 8*ptcnt.
3390  *
3391  * For each blue, all yet unprocessed HStems affected are instructed. Ghost
3392  * hints are reckognised. If there is at least one stem hint in given blue zone,
3393  * autoinstructor will seek for other interesting features, so there is no need
3394  * to hint them explicitly.
3395  *
3396  * TODO! We currently instruct hints dependent on those controlled by blues.
3397  * This may be not always corrrect (e.g. if a dependent hint is itself
3398  * controlled by blue zone - possibly even different). Research needed.
3399  *
3400  * Important notes:
3401  *
3402  * The zone count must be set to 2, the twilight point count must be nonzero.
3403  * This is done automagically in init_maxp(), otherwise this method wouldn't
3404  * work at all. Currently there is only one twilight point used, but there
3405  * may be needed one or even two points per blue zone if some advanced snapping
3406  * and counter managing is to be done.
3407  *
3408  * Snapping relies on function 0 in FPGM, see init_fpgm().
3409  *
3410  * Using MIAP (single cvt, relying on cut-in) instead of twilight points
3411  * causes overshoots to appear/disappear inconsistently at small pixel sizes.
3412  * This flickering is disastrous to soft, wavy horizontal lines. We could use
3413  * any glyph's point at needed height, but we're not certain we'll find any.
3414  *
3415  * The inner (leftwards) contours aren't snapped to the blue zone.
3416  * This could have created weird artifacts. Of course this will fail for
3417  * glyphs with wrong direction, but I won't handle it for now.
3418  *
3419  * TODO! Remind the user to correct direction or do it for him.
3420  * TODO! Try to instruct 'free points' with single push and LOOPCALL.
3421  *
3422  * If we didn't snapped any point to a blue zone, we shouldn't mark any HStem
3423  * edges done. This could made some important points on inner contours missed.
3424  *
3425  ******************************************************************************/
3426 
3427 /* Each blue zone has two TTF point indices associated with it: 'highest' and
3428  * 'lowest'. These have to be points with highest and lowest Y coordinates that
3429  * are snapped to that blue zone (directly or by horizontal stem). Currently
3430  * we register only edge.refpt. These points are later to be used for horizontal
3431  * stems' positioning.
3432  */
3433 static void update_blue_pts(int blueindex, InstrCt *ct)
3434 {
3435  BasePoint *bp = ct->bp;
3436  BlueZone *blues = ct->gic->blues;
3437 
3438  if (ct->edge.refpt == -1)
3439 return;
3440 
3441  if (blues[blueindex].highest == -1 ||
3442  bp[ct->edge.refpt].y > bp[blues[blueindex].highest].y)
3443  blues[blueindex].highest = ct->edge.refpt;
3444 
3445  if (blues[blueindex].lowest == -1 ||
3446  bp[ct->edge.refpt].y < bp[blues[blueindex].lowest].y)
3447  blues[blueindex].lowest = ct->edge.refpt;
3448 }
3449 
3450 /* It is theoretically possible that 'highest' and 'lowest' points of neighbour
3451  * blue zones overlap, and thus may spoil horizontal stems' positioning.
3452  * Here we fix this up.
3453  */
3455  if (b1->lowest > b2->lowest) b1->lowest = b2->lowest;
3456  if (b1->highest < b2->highest) b1->highest = b2->highest;
3457 }
3458 
3459 static void check_blue_pts(InstrCt *ct) {
3460  BasePoint *bp = ct->bp;
3461  BlueZone *blues = ct->gic->blues;
3462  int i, j, bluecnt = ct->gic->bluecnt;
3463 
3464  for (i=0; i<bluecnt; i++)
3465  if (blues[i].lowest != -1)
3466  for (j=0; j<bluecnt; j++)
3467  if (i != j && blues[j].lowest != -1 && SegmentsOverlap(
3468  bp[blues[i].lowest].y, bp[blues[i].highest].y,
3469  bp[blues[j].lowest].y, bp[blues[j].highest].y))
3471 }
3472 
3474  int i, is_l, ret = 0;
3475  int callargs[3] = { 0/*pt*/, 0/*cvt*/, 0 };
3476  real base, advance, tmp;
3477  real fuzz = GetBlueFuzz(ct->gic->sf);
3478  StemData *slave;
3479 
3480  /* Which edge to start at? */
3481  /* Starting at the other would usually be wrong. */
3482  if (blue->overshoot < blue->base && ( !stem->ghost || stem->width == 21 ))
3483  {
3484  is_l = false;
3485  base = stem->right.y;
3486  advance = stem->left.y;
3487  }
3488  else {
3489  is_l = true;
3490  base = stem->left.y;
3491  advance = stem->right.y;
3492  }
3493 
3494  /* This is intended as a fallback if the base edge wasn't within
3495  * this bluezone, and advance edge was.
3496  */
3497  if (!stem->ghost &&
3498  !SegmentsOverlap(base+fuzz, base-fuzz, blue->base, blue->overshoot) &&
3499  SegmentsOverlap(advance+fuzz, advance-fuzz, blue->base, blue->overshoot))
3500  {
3501  tmp = base;
3502  base = advance;
3503  advance = tmp;
3504  is_l = !is_l;
3505  }
3506 
3507  /* instruct the stem */
3508  init_stem_edge(ct, stem, is_l);
3509  if (ct->edge.refpt == -1) {
3510  for ( i=0; i<stem->dep_cnt; i++ ) {
3511  slave = stem->dependent[i].stem;
3512  /* A hack which allows single-edge hints to tie features
3513  * to remote blue zones. */
3514  if ( stem->ghost ) slave->blue = idx;
3515  if ( slave->blue == idx )
3516  ret += snap_stem_to_blue(ct, slave, blue, idx);
3517  }
3518  return( ret );
3519  }
3520  update_blue_pts(idx, ct);
3521  callargs[0] = ct->rp0 = ct->edge.refpt;
3522  callargs[1] = blue->cvtindex;
3523 
3524  if (ct->gic->fpgm_done) {
3525  ct->pt = pushpoints(ct->pt, 3, callargs);
3526  *(ct->pt)++ = CALL;
3527  }
3528  else {
3529  ct->pt = pushpoints(ct->pt, 2, callargs);
3530  *(ct->pt)++ = MIAP_rnd;
3531  }
3532 
3534  for ( i=0; i<stem->dep_cnt; i++ ) {
3535  slave = stem->dependent[i].stem;
3536  if ( slave->blue == idx ) {
3537  ret += snap_stem_to_blue(ct, slave, blue, idx);
3538  slave->master = NULL;
3539  }
3540  }
3541 
3543  instruct_serifs(ct, stem);
3544  instruct_dependent(ct, stem);
3545  update_blue_pts(idx, ct); /* this uses only refpt: who cares? */
3546  return( ret + 1 );
3547 }
3548 
3549 /* Snap stems and perhaps also some other points to given bluezone and set up
3550  * its 'highest' and 'lowest' point indices.
3551  */
3552 static void snap_to_blues(InstrCt *ct) {
3553  int i, j;
3554  int therewerestems; /* were there any HStems snapped to this blue? */
3555  StemData *stem; /* for HStems affected by blues */
3556  real base; /* for the hint */
3557  int callargs[3] = { 0/*pt*/, 0/*cvt*/, 0 };
3558  real fudge;
3559  int bluecnt=ct->gic->bluecnt;
3560  int queue[12]; /* Blue zones' indices in processing order */
3561  BlueZone *blues = ct->gic->blues;
3562  real fuzz = GetBlueFuzz(ct->gic->sf);
3563 
3564  if (bluecnt == 0)
3565 return;
3566 
3567  /* Fill the processing queue - baseline goes first, then bottom zones */
3568  /* sorted by base in ascending order, then top zones sorted in descending */
3569  /* order. I assume the blues are sorted in ascending order first. */
3570  for (i=0; (i < bluecnt) && (blues[i].base < 0); i++);
3571  queue[0] = i;
3572  for (i=0; i<queue[0]; i++) queue[i+1] = i;
3573  for (i=queue[0]+1; i<bluecnt; i++) queue[i] = bluecnt - i + queue[0];
3574 
3575  /* Process the blues. */
3576  for (i=0; i<bluecnt; i++) {
3577  therewerestems = 0;
3578 
3579  /* Process all hints with edges within current blue zone. */
3580  for ( j=0; j<ct->gd->hbundle->cnt; j++ ) {
3581  stem = ct->gd->hbundle->stemlist[j];
3582  if (stem->master != NULL || stem->blue != queue[i] || stem->ldone || stem->rdone)
3583  continue;
3584 
3585  therewerestems += snap_stem_to_blue(ct, stem, &blues[queue[i]], queue[i]);
3586  }
3587 
3588  /* Now I'll try to find points not snapped by any previous stem hint. */
3589  if (therewerestems) {
3590  base = (blues[queue[i]].base + blues[queue[i]].overshoot) / 2.0;
3591  fudge = ct->gic->fudge;
3592  ct->gic->fudge = fabs(base - blues[queue[i]].base) + fuzz;
3594  optimize_blue(ct);
3595  optimize_edge(ct);
3596 
3597  if (ct->edge.refpt == -1) {
3598  ct->gic->fudge = fudge;
3599  continue;
3600  }
3601 
3602  if (!(ct->touched[ct->edge.refpt]&tf_y || ct->affected[ct->edge.refpt]&tf_y)) {
3603  callargs[0] = ct->rp0 = ct->edge.refpt;
3604 
3605  if (ct->gic->fpgm_done) {
3606  ct->pt = pushpoints(ct->pt, 3, callargs);
3607  *(ct->pt)++ = CALL;
3608  }
3609  else {
3610  ct->pt = pushpoints(ct->pt, 2, callargs);
3611  *(ct->pt)++ = MIAP_rnd;
3612  }
3613 
3614  ct->touched[ct->edge.refpt] |= tf_y;
3615  }
3616 
3617  for (j=0; j<ct->edge.othercnt; j++) {
3618  callargs[0] = ct->rp0 = ct->edge.others[j];
3619 
3620  if (ct->gic->fpgm_done) {
3621  ct->pt = pushpoints(ct->pt, 3, callargs);
3622  *(ct->pt)++ = CALL;
3623  }
3624  else {
3625  ct->pt = pushpoints(ct->pt, 2, callargs);
3626  *(ct->pt)++ = MIAP_rnd;
3627  }
3628 
3629  ct->touched[ct->edge.others[j]] |= tf_y;
3630  }
3631 
3632  update_blue_pts(queue[i], ct);
3633 
3634  if (ct->edge.others != NULL) {
3635  free(ct->edge.others);
3636  ct->edge.others = NULL;
3637  ct->edge.othercnt = 0;
3638  }
3639 
3640  ct->gic->fudge = fudge;
3641  }
3642  }
3643 
3644  check_blue_pts(ct);
3645 }
3646 
3647 static int get_counters_cut_in(InstrCt *ct, int m1, int m2, int c1, int c2) {
3648  real s1, e1, s2, e2, width1, width2;
3649  int i, swidth1, swidth2;
3650  int EM = ct->gic->sf->ascent + ct->gic->sf->descent;
3651 
3652  s1 = (&ct->gd->points[m1].base.x)[!ct->xdir];
3653  e1 = (&ct->gd->points[m2].base.x)[!ct->xdir];
3654  s2 = (&ct->gd->points[c1].base.x)[!ct->xdir];
3655  e2 = (&ct->gd->points[c2].base.x)[!ct->xdir];
3656  width1 = e1 - s1; width2 = e2 - s2;
3657 
3658  if ( RealNear( width1, width2 ))
3659  return( 0 );
3660 
3661  for (i=7; i<32768; i++) {
3662  swidth1 = (int)rint((rint(fabs(width1)) * i * 64.0)/EM);
3663  swidth2 = (int)rint((rint(fabs(width2)) * i * 64.0)/EM);
3664  if ( fabs(swidth1 - swidth2) >= SNAP_THRESHOLD )
3665  break;
3666  }
3667  return( i );
3668 }
3669 
3670 /******************************************************************************
3671  *
3672  * High-level functions for instructing horizontal and vertical stems.
3673  * Both use 'geninstrs' for positioning single, elementary stems.
3674  *
3675  ******************************************************************************/
3676 
3677 /* geninstrs's main burden is to choose the better of two reference points
3678  * found by init_stem_edge() - one for each edge - and position it relatively
3679  * to other stems (if not already done).
3680  *
3681  * If none of the edges is positioned:
3682  * If this hint is the first, previously overlapped, or simply horizontal,
3683  * position the reference point at the base where it is using MDAP; otherwise
3684  * position the hint's base rp0 relatively to the previous hint's end using
3685  * MDRP with white minimum distance (fpgm function 1).
3686  *
3687  * Calling finish_stem() will deal with the rest of points needing explicit
3688  * positioning. Then we instruct serifs and dependent stems, if wanted.
3689  */
3690 static void geninstrs(InstrCt *ct, StemData *stem, StemData *prev, int lbase) {
3691  int shp_rp1, chg_rp0, c_m_pt1 = -1, c_m_pt2 = -1;
3692  int callargs[6];
3693  real prev_pos = 0, cur_pos;
3694 
3695  if (stem->ldone && stem->rdone)
3696  return;
3697  if ((lbase && stem->rdone) || (!lbase && stem->ldone))
3698  lbase = !lbase;
3699  init_stem_edge(ct, stem, lbase);
3700  if (ct->edge.refpt == -1) {
3701  lbase = !lbase;
3702  init_stem_edge(ct, stem, lbase);
3703  }
3704  if (ct->edge.refpt == -1)
3705  return;
3706 
3707  if (ct->rp0 < ct->gd->realcnt && ct->rp0 >= 0)
3708  prev_pos = (&ct->gd->points[ct->rp0].base.x)[!ct->xdir];
3709  cur_pos = (&ct->gd->points[ct->edge.refpt].base.x)[!ct->xdir];
3710 
3711  if (prev != NULL && stem->prev_c_m != NULL && prev->next_c_m != NULL ) {
3712  c_m_pt1 = ct->xdir ? prev->next_c_m->rightidx : prev->next_c_m->leftidx;
3713  c_m_pt2 = ct->xdir ? stem->prev_c_m->leftidx : stem->prev_c_m->rightidx;
3714  }
3715 
3716  /* Now the stem's origin must be placed in respect to others... */
3717  /* TODO! What's really needed here is an iterative procedure that */
3718  /* would preserve counters and widths, like in freetype2. */
3719  /* For horizontal stems, interpolating between blues is being be done. */
3720 
3721  if (stem->ldone || stem->rdone ) {
3722  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3723  *(ct->pt)++ = MDAP; /* sets rp0 and rp1 */
3724  shp_rp1 = use_rp1;
3725  chg_rp0 = (ct->xdir && !lbase) || (!ct->xdir && lbase);
3726  }
3727  else if (!ct->xdir) { /* horizontal stem */
3728  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3729  *(ct->pt)++ = MDAP_rnd;
3730  shp_rp1 = use_rp1;
3731  chg_rp0 = keep_old_rp0;
3732  }
3733  else if (prev == NULL) { /* first vertical stem */
3734  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3735  *(ct->pt)++ = MDRP_rp0_rnd_white;
3736  shp_rp1 = use_rp2;
3737  chg_rp0 = keep_old_rp0;
3738  }
3739  else {
3740  if (ct->gic->fpgm_done) {
3741  if ( control_counters && c_m_pt1 != -1 && c_m_pt2 != -1 ) {
3742  callargs[0] = c_m_pt1;
3743  callargs[1] = c_m_pt2;
3744  callargs[2] = ct->rp0;
3745  callargs[3] = ct->edge.refpt;
3746  callargs[4] = get_counters_cut_in(ct, c_m_pt1, c_m_pt2, ct->rp0, ct->edge.refpt);
3747  callargs[5] = 15;
3748  ct->pt = pushpoints(ct->pt, 6, callargs);
3749 
3750  } else if ( control_counters && prev != NULL && prev->leftidx != -1 && prev->rightidx != -1 ) {
3751  callargs[0] = ct->xdir ? prev->leftidx : prev->rightidx;
3752  callargs[1] = ct->edge.refpt;
3753  callargs[2] = ( cur_pos - prev_pos ) > ct->gic->fudge ? 16 : 17;
3754  ct->pt = pushpoints(ct->pt, 3, callargs);
3755 
3756  } else if ( fabs( cur_pos - prev_pos ) > ct->gic->fudge ) {
3757  ct->pt = push2nums(ct->pt, ct->edge.refpt, 1);
3758  } else {
3759  ct->pt = push2nums(ct->pt, ct->edge.refpt, 11);
3760  }
3761  *(ct->pt)++ = CALL;
3762  }
3763  else {
3764  ct->pt = pushpoint(ct->pt, ct->edge.refpt);
3765  if ( fabs( cur_pos - prev_pos ) > ct->gic->fudge )
3766  *(ct->pt)++ = MDRP_rp0_min_rnd_grey;
3767  else
3768  *(ct->pt)++ = MDRP_rp0_rnd_white;
3769  }
3770  shp_rp1 = use_rp2;
3771 
3772  /* Don't switch rp0 to the second edge. Thus, relative distance
3773  * to the next stem is be larger, and errors are hopefully lesser.
3774  * TODO! This is disputable.
3775  * TODO! For the last vstem, we probably want to switch rp0 anyway.
3776  */
3777  chg_rp0 = keep_old_rp0;
3778  }
3779  ct->rp0 = ct->edge.refpt;
3780  finish_stem(stem, shp_rp1, chg_rp0, ct);
3782  instruct_serifs(ct, stem);
3783 
3784  instruct_dependent(ct, stem);
3785 }
3786 
3787 /* High-level function for instructing horizontal stems.
3788  *
3789  * It is assumed that blues (and hstems associated with them) are already
3790  * done so that remaining stems can be interpolated between them.
3791  *
3792  * TODO! CJK hinting will probably need different function (HStemGeninstCJK?)
3793  * TODO! Instruct top and bottom bearings for fonts which have them.
3794  */
3795 static void HStemGeninst(InstrCt *ct) {
3796  BlueZone *blues = ct->gic->blues;
3797  int bluecnt = ct->gic->bluecnt;
3798  BasePoint *bp = ct->bp;
3799  StemData *stem;
3800  int i, j, rp1, rp2, opp, bpt, ept;
3801  double hbase, hend;
3802  int mdrp_end, mdrp_base, ip_base, *rpts1, *rpts2;
3803  int callargs[5];
3804 
3805  if ( ct->gd->hbundle == NULL )
3806  return;
3807  rpts1 = calloc(ct->gd->hbundle->cnt, sizeof(int));
3808  rpts2 = calloc(ct->gd->hbundle->cnt, sizeof(int));
3809 
3810  /* Interpolating between blues is splitted to two stages: first
3811  * we determine which stems can be interpolated and which cannot
3812  * and store the numbers of reference points, and then (in the
3813  * second cycle) proceed to generating actual instructions. The reason is
3814  * that we need a special handling for dependent stems: if they
3815  * can be interpolated, we process them separately, but otherwise
3816  * the normal algorithm for positioning dependent stems relatively
3817  * to their "masters" is used. It is necessary to know which method
3818  * to prefer for each stem at the time instructions are generated.
3819  */
3820  for ( i=0; i<ct->gd->hbundle->cnt; i++ )
3821  {
3822  stem = ct->gd->hbundle->stemlist[i];
3823  if (!stem->ldone && !stem->rdone)
3824  {
3825  /* Set up upper edge (hend) and lower edge (hbase). */
3826  hbase = stem->right.y;
3827  hend = stem->left.y;
3828 
3829  /* Find two points to interpolate the HStem between.
3830  rp1 = lower, rp2 = upper. */
3831  rp1 = -1;
3832  rp2 = -1;
3833 
3834  for (j=0; j<bluecnt; j++) {
3835  if (blues[j].lowest == -1) // implies blues[j].highest==-1 too
3836  continue;
3837 
3838  if (bp[blues[j].lowest].y < hbase)
3839  if (rp1==-1 || bp[rp1].y < bp[blues[j].lowest].y)
3840  rp1=blues[j].lowest;
3841 
3842  if (bp[blues[j].highest].y > hend)
3843  if (rp2==-1 || bp[rp2].y > bp[blues[j].highest].y)
3844  rp2=blues[j].highest;
3845  }
3846  rpts1[i] = rp1; rpts2[i] = rp2;
3847 
3848  /* If a dependent stem has to be positioned by interpolating
3849  * one of its edges between the edges of the master stem and
3850  * we have found reference points to interpolate it between
3851  * blues, then we prefer to interpolate it between blues. However
3852  * we keep the standard handling for other types of dependent
3853  * stems, since usually positioning relatively to the "master"
3854  * stem is more important than positioning relatively to blues
3855  * in such cases.
3856  * Exception: nested stems marked for interpolation should be
3857  * positioned by interpolating between the edges of the nesting
3858  * stem.
3859  */
3860  if (rp1!=-1 && rp2!=-1 && stem->master != NULL)
3861  for (j=0; j<stem->master->dep_cnt; j++) {
3862  if (stem->master->dependent[j].stem == stem &&
3863  stem->master->dependent[j].dep_type == 'i' &&
3864  (stem->master->left.y <= stem->left.y ||
3865  stem->master->right.y >= stem->right.y)) {
3866  stem->master = NULL;
3867  break;
3868  }
3869  }
3870  }
3871  }
3872 
3873  for ( i=0; i<ct->gd->hbundle->cnt; i++ )
3874  {
3875  stem = ct->gd->hbundle->stemlist[i];
3876  if ( stem->master != NULL )
3877  continue;
3878  if (!stem->ldone && !stem->rdone)
3879  {
3880  hbase = stem->right.y;
3881  hend = stem->left.y;
3882 
3883  rp1 = rpts1[i]; rp2 = rpts2[i];
3884  /* Reference points not found? Fall back to old method. */
3885  if (rp1==-1 || rp2==-1) {
3886  geninstrs(ct, stem, NULL, false);
3887  continue;
3888  }
3889 
3890  bpt = ept = -1;
3891  if ( !stem->ghost || stem->width == 21 ) {
3892  init_stem_edge(ct, stem, false);
3893  bpt = ct->edge.refpt;
3894  }
3895  if ( !stem->ghost || stem->width == 20 ) {
3896  init_stem_edge(ct, stem, true);
3897  ept = ct->edge.refpt;
3898  }
3899  if ( bpt == -1 && ept == -1 )
3900  continue;
3901 
3902  /* Align the stem relatively to rp0 and rp1. */
3903  mdrp_end = ept != -1 &&
3904  fabs(bp[rp2].y - hbase) < 0.2*fabs(bp[rp2].y - bp[rp1].y);
3905  mdrp_base = bpt != -1 &&
3906  fabs(bp[rp1].y - hend) < 0.2*fabs(bp[rp2].y - bp[rp1].y);
3907 
3908  if (mdrp_end || mdrp_base) {
3909  if (mdrp_end) init_stem_edge(ct, stem, true);
3910  else init_stem_edge(ct, stem, false);
3911 
3912  if (ct->edge.refpt == -1) continue;
3913 
3914  if (mdrp_end) ct->pt = push2points(ct->pt, ct->edge.refpt, rp2);
3915  else ct->pt = push2points(ct->pt, ct->edge.refpt, rp1);
3916 
3917  *(ct->pt)++ = SRP0;
3918  *(ct->pt)++ = DUP;
3919  *(ct->pt)++ = MDRP_grey;
3920  *(ct->pt)++ = MDAP_rnd;
3921  }
3922  else if ( bpt == -1 || ept == -1 ) {
3923  ip_base = ( ept == -1 );
3924  init_stem_edge(ct, stem, !ip_base);
3925  if ( ct->gic->fpgm_done ) {
3926  callargs[0] = ct->edge.refpt;
3927  callargs[1] = rp1;
3928  callargs[2] = rp2;
3929  callargs[3] = 8;
3930  ct->pt = pushnums(ct->pt, 4, callargs);
3931  *(ct->pt)++ = CALL;
3932  }
3933  else {
3934  callargs[0] = ct->edge.refpt;
3935  callargs[1] = rp1;
3936  callargs[2] = rp2;
3937  ct->pt = pushnums(ct->pt, 3, callargs);
3938  *(ct->pt)++ = SRP2;
3939  *(ct->pt)++ = SRP1;
3940  *(ct->pt)++ = DUP;
3941  *(ct->pt)++ = IP;
3942  *(ct->pt)++ = MDAP_rnd;
3943  }
3944  }
3945  else {
3946  ip_base = fabs(bp[rp2].y - hend) < fabs(bp[rp1].y - hbase);
3947  opp = ip_base ? ept : bpt;
3948  init_stem_edge(ct, stem, !ip_base);
3949 
3950  if (ct->edge.refpt == -1) continue;
3951 
3952  if ( ct->gic->fpgm_done ) {
3953  callargs[0] = opp;
3954  callargs[1] = ct->edge.refpt;
3955  callargs[2] = rp1;
3956  callargs[3] = rp2;
3957  callargs[4] = 13;
3958  ct->pt = pushnums(ct->pt, 5, callargs);
3959  *(ct->pt)++ = CALL;
3960  } else {
3961  callargs[0] = ct->edge.refpt;
3962  callargs[1] = rp1;
3963  callargs[2] = rp2;
3964  ct->pt = pushnums(ct->pt, 3, callargs);
3965  *(ct->pt)++ = SRP2;
3966  *(ct->pt)++ = SRP1;
3967  *(ct->pt)++ = DUP;
3968  *(ct->pt)++ = IP;
3969  *(ct->pt)++ = MDAP_rnd;
3970  }
3971  }
3972 
3973  ct->rp0 = ct->edge.refpt;
3976  instruct_serifs(ct, stem);
3977 
3978  instruct_dependent(ct, stem);
3979  }
3980  }
3981  free(rpts1);
3982  free(rpts2);
3983 }
3984 
3985 /*
3986  * High-level function for instructing vertical stems.
3987  *
3988  * TODO! CJK hinting may need different function (VStemGeninstCJK?)
3989  */
3990 static void VStemGeninst(InstrCt *ct) {
3991  StemData *stem, *prev=NULL;
3992  int i;
3993 
3994  if (ct->rp0 != ct->ptcnt) {
3995  ct->pt = pushpoint(ct->pt, ct->ptcnt);
3996  *(ct->pt)++ = MDAP_rnd;
3997  ct->rp0 = ct->ptcnt;
3998  }
3999 
4000  if ( ct->gd->vbundle != NULL ) {
4001  for ( i=0; i<ct->gd->vbundle->cnt; i++ ) {
4002  stem = ct->gd->vbundle->stemlist[i];
4003  if ((!stem->ldone || !stem->rdone) && stem->master == NULL) {
4004 
4005  if (prev != NULL && prev->rightidx != -1 && ct->rp0 != prev->rightidx) {
4006  ct->pt = pushpoint(ct->pt, prev->rightidx);
4007  *(ct->pt)++ = SRP0;
4008  ct->rp0 = prev->rightidx;
4009  }
4010  geninstrs(ct, stem, prev, true);
4011  prev = stem;
4012  }
4013  }
4014  }
4015 
4016  /* instruct right sidebearing */
4017  if (ct->sc->width != 0) {
4018  if ( ct->gic->fpgm_done && !control_counters ) {
4019  ct->pt = push2nums(ct->pt, ct->ptcnt+1, 1);
4020  *(ct->pt)++ = CALL;
4021  } else {
4022  /* select rp0 at the right edge of last stem - geninstrs() didn't. */
4023  /* TODO! after some time, move this to geninstrs(), to save space. */
4024  if (prev != NULL && prev->rightidx != -1 && ct->rp0 != prev->rightidx) {
4025  ct->pt = pushpoint(ct->pt, prev->rightidx);
4026  *(ct->pt)++ = SRP0;
4027  ct->rp0 = prev->rightidx;
4028  }
4029  ct->pt = pushpoint(ct->pt, ct->ptcnt+1);
4030  *(ct->pt)++ = MDRP_rp0_rnd_white;
4031  }
4032  ct->rp0 = ct->ptcnt+1;
4033  }
4034 }
4035 
4036 /******************************************************************************
4037  *
4038  * Everything related with diagonal hinting goes here
4039  *
4040  ******************************************************************************/
4041 
4042 #define DIAG_MIN_DISTANCE (0.84375)
4043 
4044 static int ds_cmp( const void *_s1, const void *_s2 ) {
4045  StemData * const *s1 = _s1, * const *s2 = _s2;
4046 
4047  BasePoint *bp1, *bp2;
4048  bp1 = (*s1)->unit.y > 0 ? &(*s1)->keypts[0]->base : &(*s1)->keypts[2]->base;
4049  bp2 = (*s2)->unit.y > 0 ? &(*s2)->keypts[0]->base : &(*s2)->keypts[2]->base;
4050  if ( bp1->x < bp2->x || ( bp1->x == bp2->x && bp1->y < bp2->y ))
4051 return( -1 );
4052  else if ( bp2->x < bp1->x || ( bp2->x == bp1->x && bp2->y < bp1->y ))
4053 return( 1 );
4054 
4055 return( 0 );
4056 }
4057 
4058 /* Takes a line defined by two points and returns a vector decribed as a
4059  * pair of x and y values, such that the value (x2 + y2) is equal to 1.
4060  * Note that the BasePoint structure is used to store the vector, although
4061  * it is not a point itself. This is just because that structure has "x"
4062  * and "y" fields which can be used for our purpose.
4063  */
4065  real catx, caty, hyp, temp;
4066  BasePoint ret;
4067 
4068  catx = top->x - bottom->x; caty = top->y - bottom->y;
4069  hyp = sqrt(( catx*catx ) + ( caty*caty ));
4070  ret.y = caty/hyp; ret.x = catx/hyp;
4071 
4072  if( orth ) {
4073  temp = ret.x; ret.x = -ret.y; ret.y = temp;
4074  }
4075 return( ret );
4076 }
4077 
4078 static int SetDStemKeyPoint( InstrCt *ct,StemData *stem,PointData *pd,int aindex ) {
4079 
4080  int nextidx, previdx, cpidx, prev_outer, next_outer, is_start;
4081  int nsidx, psidx, sidx;
4082  uint8 flag;
4083  PointData *ncpd, *pcpd, *cpd, *best = NULL;
4084  real prevdot, nextdot, cpdist;
4085 
4086  if ( pd == NULL )
4087 return( false );
4088 
4089  flag = fabs( stem->unit.y ) > fabs( stem->unit.x ) ? tf_y : tf_x;
4090  is_start = ( aindex == 0 || aindex == 2 );
4091  prevdot = ( pd->prevunit.x * stem->unit.x ) +
4092  ( pd->prevunit.y * stem->unit.y );
4093  nextdot = ( pd->nextunit.x * stem->unit.x ) +
4094  ( pd->nextunit.y * stem->unit.y );
4095  prev_outer = IsStemAssignedToPoint( pd,stem,false ) != -1 &&
4096  (( is_start && prevdot < 0 ) || ( !is_start && prevdot > 0 ));
4097  next_outer = IsStemAssignedToPoint( pd,stem,true ) != -1 &&
4098  (( is_start && nextdot < 0 ) || ( !is_start && nextdot > 0 ));
4099 
4100  if ( pd->ttfindex >= ct->gd->realcnt ) {
4101  nextidx = pd->sp->nextcpindex;
4102  previdx = pd->sp->prev->from->nextcpindex;
4103  ncpd = &ct->gd->points[nextidx];
4104  pcpd = &ct->gd->points[previdx];
4105  psidx = IsStemAssignedToPoint( pcpd,stem,true );
4106  nsidx = IsStemAssignedToPoint( ncpd,stem,false );
4107 
4108  if ( psidx == -1 && nsidx == -1 )
4109 return( false );
4110 
4111  if ( psidx > -1 && nsidx > -1 )
4112  best = ( prev_outer ) ? pcpd : ncpd;
4113  else
4114  best = ( psidx > -1 ) ? pcpd : ncpd;
4115 
4116  } else if (( !pd->sp->nonextcp && next_outer ) || ( !pd->sp->noprevcp && prev_outer )) {
4117  cpidx = ( prev_outer ) ? pd->sp->prev->from->nextcpindex : pd->sp->nextcpindex;
4118  cpd = &ct->gd->points[cpidx];
4119  sidx = IsStemAssignedToPoint( cpd,stem,prev_outer );
4120 
4121  if ( sidx != -1 ) {
4122  cpdist = fabs(( pd->base.x - cpd->base.x ) * stem->unit.x +
4123  ( pd->base.y - cpd->base.y ) * stem->unit.y );
4124  if (( cpdist > stem->clen/2 ) ||
4125  (!(ct->touched[pd->ttfindex] & flag) && !(ct->affected[pd->ttfindex] & flag) &&
4126  ( ct->touched[cpd->ttfindex] & flag || ct->affected[cpd->ttfindex] & flag )))
4127  best = cpd;
4128  }
4129  if ( best == NULL ) best = pd;
4130  } else
4131  best = pd;
4132 
4133  stem->keypts[aindex] = best;
4134 return( true );
4135 }
4136 
4137 static void AssignLineToPoint( DiagPointInfo *diagpts,StemData *stem,int idx,int is_l ) {
4138  int num, base, i;
4139  PointData *pd1, *pd2;
4140 
4141  num = diagpts[idx].count;
4142  base = ( is_l ) ? 0 : 2;
4143  pd1 = stem->keypts[base];
4144  pd2 = stem->keypts[base+1];
4145  for ( i=0; i<num; i++ ) {
4146  if ( diagpts[idx].line[i].pd1 == pd1 && diagpts[idx].line[i].pd2 == pd2 )
4147 return;
4148  }
4149 
4150  diagpts[idx].line[num].pd1 = stem->keypts[base];
4151  diagpts[idx].line[num].pd2 = stem->keypts[base+1];
4152  diagpts[idx].line[num].done = false;
4153  diagpts[idx].count++;
4154 return;
4155 }
4156 
4157 /* Convert the existing diagonal stem layout to glyph data, containing
4158  * information about points assigned to each stem. Then run on stem chunks
4159  * and associate with each point the line it should be aligned by. Note that
4160  * we have to do this on a relatively early stage, as it may be important
4161  * to know, if the given point is subject to the subsequent diagonale hinting,
4162  * before any actual processing of diagonal stems is started.
4163  */
4164 static void InitDStemData( InstrCt *ct ) {
4165  DiagPointInfo *diagpts = ct->diagpts;
4166  int i, j, idx, previdx, nextidx, num1, num2, psidx, nsidx, is_l, cnt=0;
4167  real prevlsp, prevrsp, prevlep, prevrep, lpos, rpos;
4168  GlyphData *gd;
4169  StemData *stem;
4170  PointData *ls, *rs, *le, *re, *tpd, *ppd, *npd;
4171  struct stem_chunk *chunk;
4172 
4173  gd = ct->gd;
4174 
4175  for ( i=0; i<gd->stemcnt; i++ ) {
4176  stem = &gd->stems[i];
4177  if ( stem->toobig )
4178  continue;
4179  if (( stem->unit.y > -.05 && stem->unit.y < .05 ) ||
4180  ( stem->unit.x > -.05 && stem->unit.x < .05 ))
4181  continue;
4182  if ( stem->lpcnt < 2 || stem->rpcnt < 2 )
4183  continue;
4184 
4185  prevlsp = prevrsp = 1e4;
4186  prevlep = prevrep = -1e4;
4187  ls = rs = le = re = NULL;
4188  for ( j=0; j<stem->chunk_cnt; j++ ) {
4189  chunk = &stem->chunks[j];
4190  if ( chunk->l != NULL ) {
4191  lpos = ( chunk->l->base.x - stem->left.x )*stem->unit.x +
4192  ( chunk->l->base.y - stem->left.y )*stem->unit.y;
4193  if ( lpos < prevlsp ) {
4194  ls = chunk->l; prevlsp = lpos;
4195  }
4196  if ( lpos > prevlep ) {
4197  le = chunk->l; prevlep = lpos;
4198  }
4199  }
4200  if ( chunk->r != NULL ) {
4201  rpos = ( chunk->r->base.x - stem->right.x )*stem->unit.x +
4202  ( chunk->r->base.y - stem->right.y )*stem->unit.y;
4203  if ( rpos < prevrsp ) {
4204  rs = chunk->r; prevrsp = rpos;
4205  }
4206  if ( rpos > prevrep ) {
4207  re = chunk->r; prevrep = rpos;
4208  }
4209  }
4210  }
4211 
4212  /* Swap "left" and "right" sides for vectors pointing north-east,
4213  * so that the "left" side is always determined along the x axis
4214  * rather than relatively to the vector direction */
4215  num1 = ( stem->unit.y > 0 ) ? 0 : 2;
4216  num2 = ( stem->unit.y > 0 ) ? 2 : 0;
4217  if (!SetDStemKeyPoint( ct,stem,ls,num1 ) || !SetDStemKeyPoint( ct,stem,rs,num2 ))
4218  continue;
4219 
4220  num1 = ( stem->unit.y > 0 ) ? 1 : 3;
4221  num2 = ( stem->unit.y > 0 ) ? 3 : 1;
4222  if (!SetDStemKeyPoint( ct,stem,le,num1 ) || !SetDStemKeyPoint( ct,stem,re,num2 ))
4223  continue;
4224 
4225  for ( j=0; j<gd->pcnt; j++ )
4226  gd->points[j].ticked = false;
4227  for ( j=0; j<gd->pcnt; j++ ) if ( gd->points[j].sp != NULL ) {
4228  tpd = &gd->points[j];
4229  idx = tpd->ttfindex;
4230  psidx = nsidx = -1;
4231  if ( idx < gd->realcnt ) {
4232  if ( !tpd->ticked && diagpts[idx].count < 2 && (
4233  ( psidx = IsStemAssignedToPoint( tpd,stem,false )) > -1 ||
4234  ( nsidx = IsStemAssignedToPoint( tpd,stem,true )) > -1)) {
4235 
4236  is_l = ( nsidx > -1 ) ? tpd->next_is_l[nsidx] : tpd->prev_is_l[psidx];
4237  if ( stem->unit.y < 0 ) is_l = !is_l;
4238  AssignLineToPoint( diagpts,stem,idx,is_l );
4239  tpd->ticked = true;
4240  }
4241  } else {
4242  previdx = tpd->sp->prev->from->nextcpindex;
4243  nextidx = tpd->sp->nextcpindex;
4244  ppd = &gd->points[previdx];
4245  npd = &gd->points[nextidx];
4246  if (!ppd->ticked && diagpts[previdx].count < 2 &&
4247  ( nsidx = IsStemAssignedToPoint( ppd,stem,true )) > -1 ) {
4248 
4249  is_l = ppd->next_is_l[nsidx];
4250  if ( stem->unit.y < 0 ) is_l = !is_l;
4251  AssignLineToPoint( diagpts,stem,previdx,is_l );
4252  ppd->ticked = true;
4253  }
4254  if (!npd->ticked && diagpts[nextidx].count < 2 &&
4255  ( psidx = IsStemAssignedToPoint( npd,stem,false )) > -1 ) {
4256 
4257  is_l = npd->prev_is_l[psidx];
4258  if ( stem->unit.y < 0 ) is_l = !is_l;
4259  AssignLineToPoint( diagpts,stem,nextidx,is_l );
4260  npd->ticked = true;
4261  }
4262  }
4263  }
4264  ct->diagstems[cnt++] = stem;
4265  }
4266  qsort( ct->diagstems,cnt,sizeof( StemData *),ds_cmp );
4267  ct->diagcnt = cnt;
4268 }
4269 
4270 /* Usually we have to start doing each diagonal stem from the point which
4271  * is most touched in any directions.
4272  */
4273 static int FindDiagStartPoint( StemData *stem, uint8 *touched ) {
4274  int i;
4275 
4276  for ( i=0; i<4; ++i ) {
4277  if (( touched[stem->keypts[i]->ttfindex] & tf_x ) &&
4278  ( touched[stem->keypts[i]->ttfindex] & tf_y ))
4279 return( i );
4280  }
4281 
4282  for ( i=0; i<4; ++i ) {
4283  if (( stem->unit.x > stem->unit.y &&
4284  touched[stem->keypts[i]->ttfindex] & tf_y ) ||
4285  ( stem->unit.y > stem->unit.x &&
4286  touched[stem->keypts[i]->ttfindex] & tf_x ))
4287 return( i );
4288  }
4289 
4290  for ( i=0; i<4; ++i ) {
4291  if ( touched[stem->keypts[i]->ttfindex] & ( tf_x | tf_y ))
4292 return( i );
4293  }
4294 return( 0 );
4295 }
4296 
4297 /* Check the directions at which the given point still can be moved
4298  * (i. e. has not yet been touched) and set freedom vector to that
4299  * direction in case it has not already been set.
4300  */
4301 static int SetFreedomVector( uint8 **instrs,int pnum,
4302  uint8 *touched,DiagPointInfo *diagpts,BasePoint *norm,BasePoint *fv,int pvset,int fpgm_ok ) {
4303 
4304  int i, pushpts[3];
4306  BasePoint newfv;
4307 
4308  if (( touched[pnum] & tf_d ) && !( touched[pnum] & tf_x ) && !( touched[pnum] & tf_y )) {
4309  for ( i=0 ; i<diagpts[pnum].count ; i++) {
4310  if ( diagpts[pnum].line[i].done ) {
4311  start = diagpts[pnum].line[i].pd1;
4312  end = diagpts[pnum].line[i].pd2;
4313  }
4314  }
4315 
4316  /* This should never happen */
4317  if ( start == NULL || end == NULL )
4318 return( false );
4319 
4320  newfv = GetVector( &start->base,&end->base,false );
4321  if ( !UnitsParallel( fv,&newfv,true )) {
4322  fv->x = newfv.x; fv->y = newfv.y;
4323 
4324  pushpts[0] = start->ttfindex; pushpts[1] = end->ttfindex;
4325  *instrs = pushpoints( *instrs,2,pushpts );
4326  *(*instrs)++ = 0x08; /*SFVTL[parallel]*/
4327  }
4328 
4329 return( true );
4330 
4331  } else if ( touched[pnum] & tf_x && !(touched[pnum] & tf_d) && !(touched[pnum] & tf_y)) {
4332  if (!( RealNear( fv->x,0 ) && RealNear( fv->y,1 ))) {
4333  *(*instrs)++ = 0x04; /*SFVTCA[y-axis]*/
4334  fv->x = 0; fv->y = 1;
4335  }
4336 return( true );
4337 
4338  } else if ( touched[pnum] & tf_y && !(touched[pnum] & tf_d) && !(touched[pnum] & tf_x)) {
4339  if (!( RealNear( fv->x,1 ) && RealNear( fv->y,0 ))) {
4340  *(*instrs)++ = 0x05; /*SFVTCA[x-axis]*/
4341  fv->x = 1; fv->y = 0;
4342  }
4343 return( true );
4344  } else if ( !(touched[pnum] & (tf_x|tf_y|tf_d))) {
4345  if ( !UnitsParallel( fv,norm,true )) {
4346  fv->x = norm->x; fv->y = norm->y;
4347 
4348  if ( pvset )
4349  *(*instrs)++ = 0x0E; /*SFVTPV*/
4350  else {
4351  pushpts[0] = EF2Dot14(norm->x);
4352  pushpts[1] = EF2Dot14(norm->y);
4353  if ( fpgm_ok ) {
4354  pushpts[2] = 21;
4355  *instrs = pushpoints( *instrs,3,pushpts );
4356  *(*instrs)++ = CALL; /* aspect-ratio correction */
4357  } else
4358  *instrs = pushpoints( *instrs,2,pushpts );
4359 
4360  *(*instrs)++ = 0x0b; /* SFVFS */
4361  }
4362  }
4363 return( true );
4364  }
4365 return( false );
4366 }
4367 
4368 static int MarkLineFinished( int pnum,int startnum,int endnum,DiagPointInfo *diagpts ) {
4369  int i;
4370 
4371  for ( i=0; i<diagpts[pnum].count; i++ ) {
4372  if (( diagpts[pnum].line[i].pd1->ttfindex == startnum ) &&
4373  ( diagpts[pnum].line[i].pd2->ttfindex == endnum )) {
4374 
4375  diagpts[pnum].line[i].done = 2;
4376 return( true );
4377  }
4378  }
4379 return( false );
4380 }
4381 
4383  int pt,int refpt,int firstedge,int cvt,BasePoint *fv ) {
4384  uint8 *instrs, *touched;
4385  DiagPointInfo *diagpts;
4386 
4387  diagpts = ct->diagpts;
4388  touched = ct->touched;
4389  instrs = ct->pt;
4390 
4391  if ( SetFreedomVector( &instrs,pt,touched,diagpts,&stem->l_to_r,fv,true,
4392  ct->gic->fpgm_done && ct->gic->prep_done )) {
4393  if ( refpt == -1 ) {
4394  if (( fv->x == 1 && !( touched[pt] & tf_x )) ||
4395  ( fv->y