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