1 /*
2 * Copyright 2014 Adobe Systems Incorporated (http://www.adobe.com/).
3 * All Rights Reserved.
4 *
5 * This software is licensed as OpenSource, under the Apache License, Version
6 * 2.0.
7 * This license is available at: http://opensource.org/licenses/Apache-2.0.
8 */
9
10 #include "ac.h"
11 #include "bbox.h"
12
13 static bool mergeMain;
14
15 static PathElt*
GetSubPathNxt(PathElt * e)16 GetSubPathNxt(PathElt* e)
17 {
18 if (e->type == CLOSEPATH)
19 return GetDest(e);
20 return e->next;
21 }
22
23 static PathElt*
GetSubPathPrv(PathElt * e)24 GetSubPathPrv(PathElt* e)
25 {
26 if (e->type == MOVETO)
27 e = GetClosedBy(e);
28 return e->prev;
29 }
30
31 static HintVal*
FindClosestVal(HintVal * sLst,Fixed loc)32 FindClosestVal(HintVal* sLst, Fixed loc)
33 {
34 Fixed dist = FixInt(10000);
35 HintVal* best = NULL;
36 while (sLst != NULL) {
37 Fixed bot, top, d;
38 bot = sLst->vLoc1;
39 top = sLst->vLoc2;
40 if (bot > top) {
41 Fixed tmp = bot;
42 bot = top;
43 top = tmp;
44 }
45 if (loc >= bot && loc <= top) {
46 best = sLst;
47 break;
48 }
49 if (loc < bot)
50 d = bot - loc;
51 else
52 d = loc - top;
53 if (d < dist) {
54 dist = d;
55 best = sLst;
56 }
57 sLst = sLst->vNxt;
58 }
59 return best;
60 }
61
62 static void
CpyHHint(PathElt * e)63 CpyHHint(PathElt* e)
64 {
65 Fixed x1, y1;
66 HintVal* best;
67 GetEndPoint(e, &x1, &y1);
68 best = FindClosestVal(gHPrimary, y1);
69 if (best != NULL)
70 AddHPair(best, 'b');
71 }
72
73 static void
CpyVHint(PathElt * e)74 CpyVHint(PathElt* e)
75 {
76 Fixed x1, y1;
77 HintVal* best;
78 GetEndPoint(e, &x1, &y1);
79 best = FindClosestVal(gVPrimary, x1);
80 if (best != NULL)
81 AddVPair(best, 'y');
82 }
83
84 static void
PruneHintSegs(PathElt * e,bool hFlg)85 PruneHintSegs(PathElt* e, bool hFlg)
86 {
87 SegLnkLst *lst, *nxt, *prv;
88 HintSeg* seg;
89 lst = hFlg ? e->Hs : e->Vs;
90 prv = NULL;
91 while (lst != NULL) {
92 HintVal* val = NULL;
93 SegLnk* lnk = lst->lnk;
94 if (lnk != NULL) {
95 seg = lnk->seg;
96 if (seg != NULL)
97 val = seg->sLnk;
98 }
99 nxt = lst->next;
100 if (val == NULL) { /* prune this one */
101 if (prv == NULL) {
102 if (hFlg)
103 e->Hs = nxt;
104 else
105 e->Vs = nxt;
106 } else
107 prv->next = nxt;
108 lst = nxt;
109 } else {
110 prv = lst;
111 lst = nxt;
112 }
113 }
114 }
115
116 void
PruneElementHintSegs(void)117 PruneElementHintSegs(void)
118 {
119 PathElt* e;
120 e = gPathStart;
121 while (e != NULL) {
122 PruneHintSegs(e, true);
123 PruneHintSegs(e, false);
124 e = e->next;
125 }
126 }
127
128 #define ElmntHintSegLst(e, hFlg) (hFlg) ? (e)->Hs : (e)->Vs
129
130 static void
RemLnk(PathElt * e,bool hFlg,SegLnkLst * rm)131 RemLnk(PathElt* e, bool hFlg, SegLnkLst* rm)
132 {
133 SegLnkLst *lst, *prv, *nxt;
134 lst = hFlg ? e->Hs : e->Vs;
135 prv = NULL;
136 while (lst != NULL) {
137 nxt = lst->next;
138 if (lst == rm) {
139 if (prv == NULL) {
140 if (hFlg)
141 e->Hs = nxt;
142 else
143 e->Vs = nxt;
144 } else
145 prv->next = nxt;
146 return;
147 }
148 prv = lst;
149 lst = nxt;
150 }
151 LogMsg(LOGERROR, NONFATALERROR, "Badly formatted segment list.");
152 }
153
154 static bool
AlreadyOnList(HintVal * v,HintVal * lst)155 AlreadyOnList(HintVal* v, HintVal* lst)
156 {
157 while (lst != NULL) {
158 if (v == lst)
159 return true;
160 lst = lst->vNxt;
161 }
162 return false;
163 }
164
165 static void
AutoVSeg(HintVal * sLst)166 AutoVSeg(HintVal* sLst)
167 {
168 AddVPair(sLst, 'y');
169 }
170
171 static void
AutoHSeg(HintVal * sLst)172 AutoHSeg(HintVal* sLst)
173 {
174 AddHPair(sLst, 'b');
175 }
176
177 static void
AddHHinting(HintVal * h)178 AddHHinting(HintVal* h)
179 {
180 if (gUseH || AlreadyOnList(h, gHHinting))
181 return;
182 h->vNxt = gHHinting;
183 gHHinting = h;
184 AutoHSeg(h);
185 }
186
187 static void
AddVHinting(HintVal * v)188 AddVHinting(HintVal* v)
189 {
190 if (gUseV || AlreadyOnList(v, gVHinting))
191 return;
192 v->vNxt = gVHinting;
193 gVHinting = v;
194 AutoVSeg(v);
195 }
196
197 static int32_t
TestHint(HintSeg * s,HintVal * hintList,bool flg,bool doLst)198 TestHint(HintSeg* s, HintVal* hintList, bool flg, bool doLst)
199 {
200 /* -1 means already in hintList; 0 means conflicts; 1 means ok to add */
201 HintVal *v, *clst;
202 Fixed top, bot, vT, vB, loc;
203 if (s == NULL)
204 return -1;
205 v = s->sLnk;
206 loc = s->sLoc;
207 if (v == NULL)
208 return -1;
209 vT = top = v->vLoc2;
210 vB = bot = v->vLoc1;
211 if (v->vGhst) { /* collapse width for conflict test */
212 if (v->vSeg1->sType == sGHOST)
213 bot = top;
214 else
215 top = bot;
216 }
217 {
218 int32_t cnt = 0;
219 clst = hintList;
220 while (clst != NULL) {
221 if (++cnt > 100) {
222 LogMsg(LOGDEBUG, OK, "Loop in hintlist for TestHint.");
223 return 0;
224 }
225 clst = clst->vNxt;
226 }
227 }
228 if (v->vGhst) {
229 bool loc1;
230 /* if best value for segment uses a ghost, and
231 segment loc is already in the hintList, then return -1 */
232 clst = hintList;
233 if (abs(loc - vT) < abs(loc - vB)) {
234 loc1 = false;
235 loc = vT;
236 } else {
237 loc1 = true;
238 loc = vB;
239 }
240 while (clst != NULL) {
241 if ((loc1 ? clst->vLoc1 : clst->vLoc2) == loc)
242 return -1;
243 clst = clst->vNxt;
244 if (!doLst)
245 break;
246 }
247 }
248 if (flg) {
249 top += gBandMargin;
250 bot -= gBandMargin;
251 } else {
252 top -= gBandMargin;
253 bot += gBandMargin;
254 }
255 while (hintList != NULL) { /* check for conflict */
256 Fixed cTop = hintList->vLoc2;
257 Fixed cBot = hintList->vLoc1;
258 if (vB == cBot && vT == cTop) {
259 return -1;
260 }
261 if (hintList->vGhst) { /* collapse width for conflict test */
262 if (hintList->vSeg1->sType == sGHOST)
263 cBot = cTop;
264 else
265 cTop = cBot;
266 }
267 if ((flg && (cBot <= top) && (cTop >= bot)) ||
268 (!flg && (cBot >= top) && (cTop <= bot))) {
269 return 0;
270 }
271 hintList = hintList->vNxt;
272 if (!doLst)
273 break;
274 }
275 return 1;
276 }
277
278 #define TestHHintLst(h) TestHintLst(h, gHHinting, false, true)
279 #define TestVHintLst(v) TestHintLst(v, gVHinting, true, true)
280
281 int
TestHintLst(SegLnkLst * lst,HintVal * hintList,bool flg,bool doLst)282 TestHintLst(SegLnkLst* lst, HintVal* hintList, bool flg, bool doLst)
283 {
284 /* -1 means already in hintList; 0 means conflicts; 1 means ok to add */
285 int result, cnt;
286 result = -1;
287 cnt = 0;
288 while (lst != NULL) {
289 int i = TestHint(lst->lnk->seg, hintList, flg, doLst);
290 if (i == 0) {
291 result = 0;
292 break;
293 }
294 if (i == 1)
295 result = 1;
296 lst = lst->next;
297 if (++cnt > 100) {
298 LogMsg(WARNING, OK, "Looping in TestHintLst.");
299 return 0;
300 }
301 }
302 return result;
303 }
304
305 #define FixedMidPoint(m, a, b) \
306 (m).x = ((a).x + (b).x) >> 1; \
307 (m).y = ((a).y + (b).y) >> 1
308
309 #define FixedBezDiv(a0, a1, a2, a3, b0, b1, b2, b3) \
310 FixedMidPoint(b2, a2, a3); \
311 FixedMidPoint(a3, a1, a2); \
312 FixedMidPoint(a1, a0, a1); \
313 FixedMidPoint(a2, a1, a3); \
314 FixedMidPoint(b1, a3, b2); \
315 FixedMidPoint(a3, a2, b1);
316
317 bool
ResolveConflictBySplit(PathElt * e,bool Hflg,SegLnkLst * lnk1,SegLnkLst * lnk2)318 ResolveConflictBySplit(PathElt* e, bool Hflg, SegLnkLst* lnk1, SegLnkLst* lnk2)
319 {
320 /* insert new pathelt immediately following e */
321 /* e gets first half of split; new gets second */
322 /* e gets lnk1 in Hs or Vs; new gets lnk2 */
323 PathElt* new;
324 Cd d0, d1, d2, d3, d4, d5, d6, d7;
325 if (e->type != CURVETO || e->isFlex)
326 return false;
327 ReportSplit(e);
328 new = (PathElt*)Alloc(sizeof(PathElt));
329 new->next = e->next;
330 e->next = new;
331 new->prev = e;
332 if (new->next == NULL)
333 gPathEnd = new;
334 else
335 new->next->prev = new;
336 if (Hflg) {
337 e->Hs = lnk1;
338 new->Hs = lnk2;
339 } else {
340 e->Vs = lnk1;
341 new->Vs = lnk2;
342 }
343 if (lnk1 != NULL)
344 lnk1->next = NULL;
345 if (lnk2 != NULL)
346 lnk2->next = NULL;
347 new->type = CURVETO;
348 GetEndPoint(e->prev, &d0.x, &d0.y);
349 d1.x = e->x1;
350 d1.y = e->y1;
351 d2.x = e->x2;
352 d2.y = e->y2;
353 d3.x = e->x3;
354 d3.y = e->y3;
355 d4 = d0;
356 d5 = d1;
357 d6 = d2;
358 d7 = d3;
359 new->x3 = d3.x;
360 new->y3 = d3.y;
361 FixedBezDiv(d4, d5, d6, d7, d0, d1, d2, d3);
362 e->x1 = d5.x;
363 e->y1 = d5.y;
364 e->x2 = d6.x;
365 e->y2 = d6.y;
366 e->x3 = d7.x;
367 e->y3 = d7.y;
368 new->x1 = d1.x;
369 new->y1 = d1.y;
370 new->x2 = d2.x;
371 new->y2 = d2.y;
372 return true;
373 }
374
375 static void
RemDupLnks(PathElt * e,bool Hflg)376 RemDupLnks(PathElt* e, bool Hflg)
377 {
378 SegLnkLst *l1, *l2, *l2nxt;
379 l1 = Hflg ? e->Hs : e->Vs;
380 while (l1 != NULL) {
381 l2 = l1->next;
382 while (l2 != NULL) {
383 l2nxt = l2->next;
384 if (l1->lnk->seg == l2->lnk->seg)
385 RemLnk(e, Hflg, l2);
386 l2 = l2nxt;
387 }
388 l1 = l1->next;
389 }
390 }
391
392 #define OkToRemLnk(loc, Hflg, spc) \
393 (!(Hflg) || (spc) == 0 || \
394 (!InBlueBand((loc), gLenTopBands, gTopBands) && \
395 !InBlueBand((loc), gLenBotBands, gBotBands)))
396
397 /* The changes made here were to fix a problem in MinisterLight/E.
398 The top left point was not getting hinted. */
399 static bool
TryResolveConflict(PathElt * e,bool Hflg)400 TryResolveConflict(PathElt* e, bool Hflg)
401 {
402 int32_t typ;
403 SegLnkLst *lst, *lnk1, *lnk2;
404 HintSeg *seg, *seg1, *seg2;
405 HintVal *val1, *val2;
406 Fixed lc1, lc2, loc0, loc1, loc2, loc3, x0, y0, x1, y1;
407 RemDupLnks(e, Hflg);
408 typ = e->type;
409 if (typ == MOVETO)
410 GetEndPoints(GetClosedBy(e), &x0, &y0, &x1, &y1);
411 else if (typ == CURVETO) {
412 x0 = e->x1;
413 y0 = e->y1;
414 x1 = e->x3;
415 y1 = e->y3;
416 } else
417 GetEndPoints(e, &x0, &y0, &x1, &y1);
418 loc1 = Hflg ? y0 : x0;
419 loc2 = Hflg ? y1 : x1;
420 lst = Hflg ? e->Hs : e->Vs;
421 seg1 = lst->lnk->seg;
422 lc1 = seg1->sLoc;
423 lnk1 = lst;
424 lst = lst->next;
425 seg2 = lst->lnk->seg;
426 lc2 = seg2->sLoc;
427 lnk2 = lst;
428 if (lc1 == loc1 || lc2 == loc2) {
429 /* do nothing */
430 } else if (abs(lc1 - loc1) > abs(lc1 - loc2) ||
431 abs(lc2 - loc2) > abs(lc2 - loc1)) {
432 seg = seg1;
433 seg1 = seg2;
434 seg2 = seg;
435 lst = lnk1;
436 lnk1 = lnk2;
437 lnk2 = lst;
438 }
439 val1 = seg1->sLnk;
440 val2 = seg2->sLnk;
441 if (val1->vVal < FixInt(50) && OkToRemLnk(loc1, Hflg, val1->vSpc)) {
442 RemLnk(e, Hflg, lnk1);
443 ReportRemConflict(e);
444 return true;
445 }
446 if (val2->vVal < FixInt(50) && val1->vVal > val2->vVal * 20 &&
447 OkToRemLnk(loc2, Hflg, val2->vSpc)) {
448 RemLnk(e, Hflg, lnk2);
449 ReportRemConflict(e);
450 return true;
451 }
452 if (typ != CURVETO || ((((Hflg && IsHorizontal(x0, y0, x1, y1)) ||
453 (!Hflg && IsVertical(x0, y0, x1, y1)))) &&
454 OkToRemLnk(loc1, Hflg, val1->vSpc))) {
455 RemLnk(e, Hflg, lnk1);
456 ReportRemConflict(e);
457 return true;
458 }
459 GetEndPoints(GetSubPathPrv(e), &x0, &y0, &x1, &y1);
460 loc0 = Hflg ? y0 : x0;
461 if (ProdLt0(loc2 - loc1, loc0 - loc1)) {
462 RemLnk(e, Hflg, lnk1);
463 ReportRemConflict(e);
464 return true;
465 }
466 GetEndPoint(GetSubPathNxt(e), &x1, &y1);
467 loc3 = Hflg ? y1 : x1;
468 if (ProdLt0(loc3 - loc2, loc1 - loc2)) {
469 RemLnk(e, Hflg, lnk2);
470 ReportRemConflict(e);
471 return true;
472 }
473 if ((loc2 == val2->vLoc1 || loc2 == val2->vLoc2) && loc1 != val1->vLoc1 &&
474 loc1 != val1->vLoc2) {
475 RemLnk(e, Hflg, lnk1);
476 ReportRemConflict(e);
477 return true;
478 }
479 if ((loc1 == val1->vLoc1 || loc1 == val1->vLoc2) && loc2 != val2->vLoc1 &&
480 loc2 != val2->vLoc2) {
481 RemLnk(e, Hflg, lnk2);
482 ReportRemConflict(e);
483 return true;
484 }
485 if (gEditGlyph && ResolveConflictBySplit(e, Hflg, lnk1, lnk2))
486 return true;
487 else
488 return false;
489 }
490
491 static bool
CheckHintSegs(PathElt * e,bool flg,bool Hflg)492 CheckHintSegs(PathElt* e, bool flg, bool Hflg)
493 {
494 SegLnkLst* lst;
495 SegLnkLst* lst2;
496 HintSeg* seg;
497 HintVal* val;
498 lst = Hflg ? e->Hs : e->Vs;
499 while (lst != NULL) {
500 lst2 = lst->next;
501 if (lst2 != NULL) {
502 seg = lst->lnk->seg;
503 val = seg->sLnk;
504 if (val != NULL && TestHintLst(lst2, val, flg, false) == 0) {
505 if (TryResolveConflict(e, Hflg))
506 return CheckHintSegs(e, flg, Hflg);
507 if (Hflg)
508 e->Hs = NULL;
509 else
510 e->Vs = NULL;
511 return true;
512 }
513 }
514 lst = lst2;
515 }
516 return false;
517 }
518
519 static void
CheckElmntHintSegs(void)520 CheckElmntHintSegs(void)
521 {
522 PathElt* e;
523 e = gPathStart;
524 while (e != NULL) {
525 if (!CheckHintSegs(e, false, true))
526 CheckHintSegs(e, true, false);
527 e = e->next;
528 }
529 }
530 static bool
HintLstsClash(SegLnkLst * lst1,SegLnkLst * lst2,bool flg)531 HintLstsClash(SegLnkLst* lst1, SegLnkLst* lst2, bool flg)
532 {
533 while (lst1 != NULL) {
534 HintSeg* seg = lst1->lnk->seg;
535 HintVal* val = seg->sLnk;
536 if (val != NULL) {
537 SegLnkLst* lst = lst2;
538 while (lst != NULL) {
539 if (TestHintLst(lst, val, flg, false) == 0) {
540 return true;
541 }
542 lst = lst->next;
543 }
544 }
545 lst1 = lst1->next;
546 }
547 return false;
548 }
549
550 static SegLnkLst*
BestFromLsts(SegLnkLst * lst1,SegLnkLst * lst2)551 BestFromLsts(SegLnkLst* lst1, SegLnkLst* lst2)
552 {
553 SegLnkLst* bst = NULL;
554 Fixed bstval = 0;
555 int32_t i;
556 for (i = 0; i < 2; i++) {
557 SegLnkLst* lst = i ? lst1 : lst2;
558 while (lst != NULL) {
559 HintSeg* seg = lst->lnk->seg;
560 HintVal* val = seg->sLnk;
561 if (val != NULL && val->vVal > bstval) {
562 bst = lst;
563 bstval = val->vVal;
564 }
565 lst = lst->next;
566 }
567 }
568 return bst;
569 }
570
571 static bool
HintsClash(PathElt * e,PathElt * p,SegLnkLst ** hLst,SegLnkLst ** vLst,SegLnkLst ** phLst,SegLnkLst ** pvLst)572 HintsClash(PathElt* e, PathElt* p, SegLnkLst** hLst, SegLnkLst** vLst,
573 SegLnkLst** phLst, SegLnkLst** pvLst)
574 {
575 bool clash = false;
576 SegLnkLst *bst, *new;
577 if (HintLstsClash(*hLst, *phLst, false)) {
578 clash = true;
579 bst = BestFromLsts(*hLst, *phLst);
580 if (bst) {
581 new = (SegLnkLst*)Alloc(sizeof(SegLnkLst));
582 new->next = NULL;
583 new->lnk = bst->lnk;
584 } else
585 new = NULL;
586 e->Hs = p->Hs = *hLst = *phLst = new;
587 }
588 if (HintLstsClash(*vLst, *pvLst, true)) {
589 clash = true;
590 bst = BestFromLsts(*vLst, *pvLst);
591 if (bst) {
592 new = (SegLnkLst*)Alloc(sizeof(SegLnkLst));
593 new->next = NULL;
594 new->lnk = bst->lnk;
595 } else
596 new = NULL;
597 e->Vs = p->Vs = *vLst = *pvLst = new;
598 }
599 return clash;
600 }
601
602 static void
GetHintLsts(PathElt * e,SegLnkLst ** phLst,SegLnkLst ** pvLst,int32_t * ph,int32_t * pv)603 GetHintLsts(PathElt* e, SegLnkLst** phLst, SegLnkLst** pvLst, int32_t* ph,
604 int32_t* pv)
605 {
606 SegLnkLst *hLst, *vLst;
607 int32_t h, v;
608 if (gUseH) {
609 hLst = NULL;
610 h = -1;
611 } else {
612 hLst = e->Hs;
613 if (hLst == NULL)
614 h = -1;
615 else
616 h = TestHHintLst(hLst);
617 }
618 if (gUseV) {
619 vLst = NULL;
620 v = -1;
621 } else {
622 vLst = e->Vs;
623 if (vLst == NULL)
624 v = -1;
625 else
626 v = TestVHintLst(vLst);
627 }
628 *pvLst = vLst;
629 *phLst = hLst;
630 *ph = h;
631 *pv = v;
632 }
633
634 static void
ReHintBounds(PathElt * e)635 ReHintBounds(PathElt* e)
636 {
637 if (!gUseH) {
638 if (gHHinting == NULL)
639 CpyHHint(e);
640 if (mergeMain)
641 MergeFromMainHints('b');
642 }
643 if (!gUseV) {
644 if (gVHinting == NULL)
645 CpyVHint(e);
646 if (mergeMain)
647 MergeFromMainHints('y');
648 }
649 }
650
651 static void
AddHintLst(SegLnkLst * lst,bool vert)652 AddHintLst(SegLnkLst* lst, bool vert)
653 {
654 while (lst != NULL) {
655 HintSeg* seg = lst->lnk->seg;
656 HintVal* val = seg->sLnk;
657 if (vert)
658 AddVHinting(val);
659 else
660 AddHHinting(val);
661 lst = lst->next;
662 }
663 }
664
665 static void
StartNewHinting(PathElt * e,SegLnkLst * hLst,SegLnkLst * vLst)666 StartNewHinting(PathElt* e, SegLnkLst* hLst, SegLnkLst* vLst)
667 {
668 ReHintBounds(e);
669 if (e->newhints != 0) {
670 LogMsg(LOGERROR, NONFATALERROR, "Uninitialized extra hints list.");
671 }
672 XtraHints(e);
673 if (gUseV)
674 CopyMainV();
675 if (gUseH)
676 CopyMainH();
677 gHHinting = gVHinting = NULL;
678 if (!gUseH)
679 AddHintLst(hLst, false);
680 if (!gUseV)
681 AddHintLst(vLst, true);
682 }
683
684 static bool
IsIn(int32_t h,int32_t v)685 IsIn(int32_t h, int32_t v)
686 {
687 return (h == -1 && v == -1);
688 }
689
690 static bool
IsOk(int32_t h,int32_t v)691 IsOk(int32_t h, int32_t v)
692 {
693 return (h != 0 && v != 0);
694 }
695
696 #define AddIfNeedV(v, vLst) \
697 if (!gUseV && v == 1) \
698 AddHintLst(vLst, true)
699 #define AddIfNeedH(h, hLst) \
700 if (!gUseH && h == 1) \
701 AddHintLst(hLst, false)
702
703 static void
SetHHints(HintVal * lst)704 SetHHints(HintVal* lst)
705 {
706 if (gUseH)
707 return;
708 gHHinting = lst;
709 while (lst != NULL) {
710 AutoHSeg(lst);
711 lst = lst->vNxt;
712 }
713 }
714
715 static void
SetVHints(HintVal * lst)716 SetVHints(HintVal* lst)
717 {
718 if (gUseV)
719 return;
720 gVHinting = lst;
721 while (lst != NULL) {
722 AutoVSeg(lst);
723 lst = lst->vNxt;
724 }
725 }
726
727 HintVal*
CopyHints(HintVal * lst)728 CopyHints(HintVal* lst)
729 {
730 HintVal* vlst;
731 int cnt;
732 vlst = NULL;
733 cnt = 0;
734 while (lst != NULL) {
735 HintVal* v = (HintVal*)Alloc(sizeof(HintVal));
736 *v = *lst;
737 v->vNxt = vlst;
738 vlst = v;
739 if (++cnt > 100) {
740 LogMsg(WARNING, OK, "Loop in CopyHints.");
741 return vlst;
742 }
743 lst = lst->vNxt;
744 }
745 return vlst;
746 }
747
748 static bool
IsFlare(Fixed loc,PathElt * e,PathElt * n,bool Hflg)749 IsFlare(Fixed loc, PathElt* e, PathElt* n, bool Hflg)
750 {
751 Fixed x, y;
752 while (e != n) {
753 GetEndPoint(e, &x, &y);
754 if ((Hflg && abs(y - loc) > gMaxFlare) ||
755 (!Hflg && abs(x - loc) > gMaxFlare))
756 return false;
757 e = GetSubPathNxt(e);
758 }
759 return true;
760 }
761
762 static bool
IsTopSegOfVal(Fixed loc,Fixed top,Fixed bot)763 IsTopSegOfVal(Fixed loc, Fixed top, Fixed bot)
764 {
765 Fixed d1, d2;
766 d1 = top - loc;
767 d2 = bot - loc;
768 return (abs(d1) <= abs(d2)) ? true : false;
769 }
770
771 static void
RemFlareLnk(PathElt * e,bool hFlg,SegLnkLst * rm,PathElt * e2,int32_t i)772 RemFlareLnk(PathElt* e, bool hFlg, SegLnkLst* rm, PathElt* e2, int32_t i)
773 {
774 RemLnk(e, hFlg, rm);
775 ReportRemFlare(e, e2, hFlg, i);
776 }
777
778 bool
CompareValues(HintVal * val1,HintVal * val2,int32_t factor,int32_t ghstshift)779 CompareValues(HintVal* val1, HintVal* val2, int32_t factor, int32_t ghstshift)
780 {
781 Fixed v1 = val1->vVal, v2 = val2->vVal, mx;
782 mx = NUMMAX(v1, v2);
783 while (mx < FIXED_MAX / 2) {
784 mx *= 2;
785 v1 *= 2;
786 v2 *= 2;
787 }
788 if (ghstshift > 0 && val1->vGhst != val2->vGhst) {
789 if (val1->vGhst)
790 v1 >>= ghstshift;
791 if (val2->vGhst)
792 v2 >>= ghstshift;
793 }
794 if ((val1->vSpc > 0 && val2->vSpc > 0) ||
795 (val1->vSpc == 0 && val2->vSpc == 0))
796 return v1 > v2;
797 if (val1->vSpc > 0)
798 return (v1 < FIXED_MAX / factor) ? (v1 * factor > v2)
799 : (v1 > v2 / factor);
800 return (v2 < FIXED_MAX / factor) ? (v1 > v2 * factor) : (v1 / factor > v2);
801 }
802
803 static void
RemFlares(bool Hflg)804 RemFlares(bool Hflg)
805 {
806 SegLnkLst *lst1, *lst2, *nxt1, *nxt2;
807 PathElt *e, *n;
808 HintSeg *seg1, *seg2;
809 HintVal *val1, *val2;
810 Fixed diff;
811 bool nxtE;
812 bool Nm1, Nm2;
813 if (Hflg) {
814 Nm1 = true;
815 Nm2 = false;
816 } else {
817 Nm1 = false;
818 Nm2 = true;
819 }
820 e = gPathStart;
821 while (e != NULL) {
822 if (Nm1 ? e->Hs == NULL : e->Vs == NULL) {
823 e = e->next;
824 continue;
825 }
826 /* e now is an element with Nm1 prop */
827 n = GetSubPathNxt(e);
828 nxtE = false;
829 while (n != e && !nxtE) {
830 if (Nm1 ? n->Hs != NULL : n->Vs != NULL) {
831 lst1 = ElmntHintSegLst(e, Nm1);
832 while (lst1 != NULL) {
833 seg1 = lst1->lnk->seg;
834 nxt1 = lst1->next;
835 lst2 = ElmntHintSegLst(n, Nm1);
836 while (lst2 != NULL) {
837 seg2 = lst2->lnk->seg;
838 nxt2 = lst2->next;
839 if (seg1 != NULL && seg2 != NULL) {
840 diff = seg1->sLoc - seg2->sLoc;
841 if (abs(diff) > gMaxFlare) {
842 nxtE = true;
843 goto Nxt2;
844 }
845 if (!IsFlare(seg1->sLoc, e, n, Hflg)) {
846 nxtE = true;
847 goto Nxt2;
848 }
849 val1 = seg1->sLnk;
850 val2 = seg2->sLnk;
851 if (diff != 0 &&
852 IsTopSegOfVal(seg1->sLoc, val1->vLoc2,
853 val1->vLoc1) ==
854 IsTopSegOfVal(seg2->sLoc, val2->vLoc2,
855 val2->vLoc1)) {
856 if (CompareValues(val1, val2, spcBonus, 0)) {
857 /* This change was made to fix flares in
858 * Bodoni2. */
859 if (val2->vSpc == 0 &&
860 val2->vVal < FixInt(1000))
861 RemFlareLnk(n, Nm1, lst2, e, 1);
862 } else if (val1->vSpc == 0 &&
863 val1->vVal < FixInt(1000)) {
864 RemFlareLnk(e, Nm1, lst1, n, 2);
865 goto Nxt1;
866 }
867 }
868 }
869 Nxt2:
870 lst2 = nxt2;
871 }
872 Nxt1:
873 lst1 = nxt1;
874 }
875 }
876 if (Nm2 ? n->Hs != NULL : n->Vs != NULL)
877 break;
878 n = GetSubPathNxt(n);
879 }
880 e = e->next;
881 }
882 }
883
884 static void
CarryIfNeed(Fixed loc,bool vert,HintVal * hints)885 CarryIfNeed(Fixed loc, bool vert, HintVal* hints)
886 {
887 HintSeg* seg;
888 HintVal* seglnk;
889 Fixed l0, l1, tmp, halfMargin;
890 if ((vert && gUseV) || (!vert && gUseH))
891 return;
892 halfMargin = FixHalfMul(gBandMargin);
893 /* DEBUG 8 BIT. Needed to double test from 10 to 20 for change in coordinate
894 * system */
895 if (halfMargin > FixInt(20))
896 halfMargin = FixInt(20);
897 while (hints != NULL) {
898 seg = hints->vSeg1;
899 if (hints->vGhst && seg->sType == sGHOST)
900 seg = hints->vSeg2;
901 if (seg == NULL)
902 goto Nxt;
903 l0 = hints->vLoc2;
904 l1 = hints->vLoc1;
905 if (l0 > l1) {
906 tmp = l1;
907 l1 = l0;
908 l0 = tmp;
909 }
910 l0 -= halfMargin;
911 l1 += halfMargin;
912 if (loc > l0 && loc < l1) {
913 seglnk = seg->sLnk;
914 seg->sLnk = hints;
915 if (vert) {
916 if (TestHint(seg, gVHinting, true, true) == 1) {
917 ReportCarry(l0, l1, loc, hints, vert);
918 AddVHinting(hints);
919 seg->sLnk = seglnk;
920 break;
921 }
922 } else if (TestHint(seg, gHHinting, false, true) == 1) {
923 ReportCarry(l0, l1, loc, hints, vert);
924 AddHHinting(hints);
925 seg->sLnk = seglnk;
926 break;
927 }
928 seg->sLnk = seglnk;
929 }
930 Nxt:
931 hints = hints->vNxt;
932 }
933 }
934
935 #define PRODIST \
936 (FixInt(100)) /* DEBUG 8 BIT. Needed to double test from 50 to 100 for \
937 change in coordinate system */
938 static void
ProHints(PathElt * e,bool hFlg,Fixed loc)939 ProHints(PathElt* e, bool hFlg, Fixed loc)
940 {
941 SegLnkLst* lst;
942 PathElt* prv;
943 lst = ElmntHintSegLst(e, hFlg);
944 if (lst == NULL)
945 return;
946 if (hFlg ? e->Hcopy : e->Vcopy)
947 return;
948 prv = e;
949 while (true) {
950 Fixed cx, cy, dst;
951 SegLnkLst* plst;
952 prv = GetSubPathPrv(prv);
953 plst = ElmntHintSegLst(prv, hFlg);
954 if (plst != NULL)
955 return;
956 GetEndPoint(prv, &cx, &cy);
957 dst = (hFlg ? cy : cx) - loc;
958 if (abs(dst) > PRODIST)
959 return;
960 if (hFlg) {
961 prv->Hs = lst;
962 prv->Hcopy = true;
963 } else {
964 prv->Vs = lst;
965 prv->Vcopy = true;
966 }
967 }
968 }
969
970 static void
PromoteHints(void)971 PromoteHints(void)
972 {
973 PathElt* e;
974 e = gPathStart;
975 while (e != NULL) {
976 Fixed cx, cy;
977 GetEndPoint(e, &cx, &cy);
978 ProHints(e, true, cy);
979 ProHints(e, false, cx);
980 e = e->next;
981 }
982 }
983
984 static void
RemPromotedHints(void)985 RemPromotedHints(void)
986 {
987 PathElt* e;
988 e = gPathStart;
989 while (e != NULL) {
990 if (e->Hcopy) {
991 e->Hs = NULL;
992 e->Hcopy = false;
993 }
994 if (e->Vcopy) {
995 e->Vs = NULL;
996 e->Vcopy = false;
997 }
998 e = e->next;
999 }
1000 }
1001
1002 static void
RemShortHints(void)1003 RemShortHints(void)
1004 {
1005 /* Must not change hints at a short element. */
1006 PathElt* e;
1007 Fixed cx, cy, ex, ey;
1008 e = gPathStart;
1009 cx = 0;
1010 cy = 0;
1011 while (e != NULL) {
1012 GetEndPoint(e, &ex, &ey);
1013 if (abs(cx - ex) < gMinHintElementLength &&
1014 abs(cy - ey) < gMinHintElementLength) {
1015 ReportRemShortHints(ex, ey);
1016 e->Hs = NULL;
1017 e->Vs = NULL;
1018 }
1019 e = e->next;
1020 cx = ex;
1021 cy = ey;
1022 }
1023 }
1024
1025 void
AutoExtraHints(bool movetoNewHints)1026 AutoExtraHints(bool movetoNewHints)
1027 {
1028 int32_t h, v, ph, pv;
1029 PathElt *e, *cp, *p;
1030 SegLnkLst *hLst, *vLst, *phLst, *pvLst;
1031 HintVal *mtVhints, *mtHhints, *prvHhints, *prvVhints;
1032
1033 bool (*Tst)(int32_t, int32_t), newHints = true;
1034 Fixed x, y;
1035
1036 mergeMain = (CountSubPaths() <= 5);
1037 e = gPathStart;
1038 LogMsg(LOGDEBUG, OK, "RemFlares");
1039 RemFlares(true);
1040 RemFlares(false);
1041 LogMsg(LOGDEBUG, OK, "CheckElmntHintSegs");
1042 CheckElmntHintSegs();
1043 LogMsg(LOGDEBUG, OK, "PromoteHints");
1044 PromoteHints();
1045 LogMsg(LOGDEBUG, OK, "RemShortHints");
1046 RemShortHints();
1047 p = NULL;
1048 Tst = IsOk; /* it is ok to add to primary hinting */
1049 LogMsg(LOGDEBUG, OK, "hint loop");
1050 mtVhints = mtHhints = NULL;
1051 while (e != NULL) {
1052 int32_t etype = e->type;
1053 if (movetoNewHints && etype == MOVETO) {
1054 StartNewHinting(e, NULL, NULL);
1055 Tst = IsOk;
1056 }
1057 if (newHints && e == p) {
1058 StartNewHinting(e, NULL, NULL);
1059 SetHHints(mtHhints);
1060 SetVHints(mtVhints);
1061 Tst = IsIn;
1062 }
1063 GetHintLsts(e, &hLst, &vLst, &h, &v);
1064 if (etype == MOVETO && IsShort(cp = GetClosedBy(e))) {
1065 GetHintLsts(p = cp->prev, &phLst, &pvLst, &ph, &pv);
1066 if (HintsClash(e, p, &hLst, &vLst, &phLst, &pvLst)) {
1067 GetHintLsts(e, &hLst, &vLst, &h, &v);
1068 GetHintLsts(p, &phLst, &pvLst, &ph, &pv);
1069 }
1070 if (!(*Tst)(ph, pv) || !(*Tst)(h, v)) {
1071 StartNewHinting(e, hLst, vLst);
1072 Tst = IsOk;
1073 ph = pv = 1; /* force add of hints for p also */
1074 } else {
1075 AddIfNeedH(h, hLst);
1076 AddIfNeedV(v, vLst);
1077 }
1078 AddIfNeedH(ph, phLst);
1079 AddIfNeedV(pv, pvLst);
1080 newHints = false; /* so can tell if added new hints in subpath */
1081 } else if (!(*Tst)(h, v)) { /* e needs new hinting */
1082 if (etype ==
1083 CLOSEPATH) { /* do not attach extra hints to closepath */
1084 e = e->prev;
1085 GetHintLsts(e, &hLst, &vLst, &h, &v);
1086 }
1087 prvHhints = CopyHints(gHHinting);
1088 prvVhints = CopyHints(gVHinting);
1089 if (!newHints) { /* this is the first extra since mt */
1090 newHints = true;
1091 mtVhints = CopyHints(prvVhints);
1092 mtHhints = CopyHints(prvHhints);
1093 }
1094 StartNewHinting(e, hLst, vLst);
1095 Tst = IsOk;
1096 if (etype == CURVETO) {
1097 x = e->x1;
1098 y = e->y1;
1099 } else
1100 GetEndPoint(e, &x, &y);
1101 CarryIfNeed(y, false, prvHhints);
1102 CarryIfNeed(x, true, prvVhints);
1103 } else { /* do not need to start new hinting */
1104 AddIfNeedH(h, hLst);
1105 AddIfNeedV(v, vLst);
1106 }
1107 e = e->next;
1108 }
1109 ReHintBounds(gPathEnd);
1110 LogMsg(LOGDEBUG, OK, "RemPromotedHints");
1111 RemPromotedHints();
1112 LogMsg(LOGDEBUG, OK, "done AutoExtraHints");
1113 }
1114