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