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 #define MAXF (1 << 15)
14 static void
AdjustVal(Fixed * pv,Fixed l1,Fixed l2,Fixed dist,Fixed d,bool hFlg)15 AdjustVal(Fixed* pv, Fixed l1, Fixed l2, Fixed dist, Fixed d, bool hFlg)
16 {
17 float v, q, r1, r2, rd;
18 /* DEBUG 8 BIT. To get the saem result as the old auothint, had to change
19 from FixedOne to FixedTwo. Since the returned weight is proportional to the
20 square of l1 and l2,
21 these need to be clamped to twice the old clamped value, else when the
22 clamped values are used, the weight comes out as 1/4 of the original value.
23 */
24 if (dist < FixTwo)
25 dist = FixTwo;
26 if (l1 < FixTwo)
27 l1 = FixTwo;
28 if (l2 < FixTwo)
29 l2 = FixTwo;
30 if (abs(l1) < MAXF)
31 r1 = (float)(l1 * l1);
32 else {
33 r1 = (float)l1;
34 r1 = r1 * r1;
35 }
36 if (abs(l2) < MAXF)
37 r2 = (float)(l2 * l2);
38 else {
39 r2 = (float)l2;
40 r2 = r2 * r2;
41 }
42 if (abs(dist) < MAXF)
43 q = (float)(dist * dist);
44 else {
45 q = (float)dist;
46 q = q * q;
47 }
48 v = (float)((1000.0f * r1 * r2) / (q * q));
49 if (d <= (hFlg ? gHBigDist : gVBigDist))
50 goto done;
51 acfixtopflt(d, &rd);
52 q = (hFlg ? gHBigDistR : gVBigDistR) / rd; /* 0 < q < 1.0 */
53 if (q <= 0.5f) {
54 v = 0.0;
55 goto done;
56 }
57 q *= q;
58 q *= q;
59 q *= q; /* raise q to 8th power */
60 v = v * q; /* if d is twice bigDist, value goes down by factor of 256 */
61 done:
62 if (v > gMaxVal)
63 v = gMaxVal;
64 else if (v > 0.0f && v < gMinVal)
65 v = gMinVal;
66 *pv = acpflttofix(&v);
67 }
68
69 static Fixed
CalcOverlapDist(Fixed d,Fixed overlaplen,Fixed minlen)70 CalcOverlapDist(Fixed d, Fixed overlaplen, Fixed minlen)
71 {
72 float r = (float)d, ro = (float)overlaplen, rm = (float)minlen;
73 r = r * ((float)(1.0f + 0.4f * (1.0f - ro / rm)));
74 d = (Fixed)r;
75 return d;
76 }
77
78 #define GapDist(d) \
79 (((d) < FixInt(127)) ? FTrunc(((d) * (d)) / 40) \
80 : ((int32_t)(((double)(d)) * (d) / (40 * 256))))
81 /* if d is >= 127.0 Fixed, then d*d will overflow the signed int 16 bit value.
82 */
83 /* DEBUG 8 BIT. No idea why d*d was divided by 20, but we need to divide it by 2
84 more to get a dist that is only 2* the old autohint value.
85 With the 8.8 fixed coordinate system, we still overflow a int32_t with
86 d*(d/40), so rather than casting this to a int32_t and then doing >>8, we need
87 to divide by 256, then cast to int32_t.
88 I also fail to understand why the original used FTrunc, which right shifts by
89 256. For the current coordinate space, which has a fractional part of 8 bits,
90 you do need to divide by 256 after doing a simple int multiply, but the
91 previous coordinate space
92 has a 7 bit Fixed fraction, and should be dividing by 128. I suspect that
93 there was a yet earlier version which used a 8 bit fraction, and this is a bug.
94 */
95
96 static void
EvalHPair(HintSeg * botSeg,HintSeg * topSeg,Fixed * pspc,Fixed * pv)97 EvalHPair(HintSeg* botSeg, HintSeg* topSeg, Fixed* pspc, Fixed* pv)
98 {
99 Fixed brght, blft, bloc, tloc, trght, tlft;
100 Fixed mndist, dist, dy;
101 bool inBotBand, inTopBand;
102 *pspc = 0;
103 brght = botSeg->sMax;
104 blft = botSeg->sMin;
105 trght = topSeg->sMax;
106 tlft = topSeg->sMin;
107 bloc = botSeg->sLoc;
108 tloc = topSeg->sLoc;
109 dy = abs(bloc - tloc);
110 if (dy < gMinDist) {
111 *pv = 0;
112 return;
113 }
114 inBotBand = InBlueBand(bloc, gLenBotBands, gBotBands);
115 inTopBand = InBlueBand(tloc, gLenTopBands, gTopBands);
116 if (inBotBand && inTopBand) { /* delete these */
117 *pv = 0;
118 return;
119 }
120 if (inBotBand || inTopBand) /* up the priority of these */
121 *pspc = FixInt(2);
122 /* left is always < right */
123 if ((tlft <= brght) && (trght >= blft)) { /* overlap */
124 Fixed overlaplen = NUMMIN(trght, brght) - NUMMAX(tlft, blft);
125 Fixed minlen = NUMMIN(trght - tlft, brght - blft);
126 if (minlen == overlaplen)
127 dist = dy;
128 else
129 dist = CalcOverlapDist(dy, overlaplen, minlen);
130 } else { /* no overlap; take closer ends */
131 Fixed dx;
132 Fixed ldst = abs(tlft - brght);
133 Fixed rdst = abs(trght - blft);
134 dx = NUMMIN(ldst, rdst);
135 dist = GapDist(dx);
136 /* extra penalty for nonoverlap changed from 7/5 to 12/5 for
137 * Perpetua/Regular/ n, r ,m and other lowercase serifs; undid change
138 * for Berthold/AkzidenzGrotesk 9/16/91; this did not make Perpetua any
139 * worse. */
140 dist += (7 * dy) / 5;
141 DEBUG_ROUND(dist) /* DEBUG 8 BIT */
142 if (dx > dy)
143 dist *= dx / dy;
144 }
145 mndist = FixTwoMul(gMinDist);
146 dist = NUMMAX(dist, mndist);
147 if (gNumHStems > 0) {
148 int i;
149 Fixed w = abs(dy);
150 for (i = 0; i < gNumHStems; i++)
151 if (w == gHStems[i]) {
152 *pspc += FixOne;
153 break;
154 }
155 }
156 AdjustVal(pv, brght - blft, trght - tlft, dist, dy, true);
157 }
158
159 static void
HStemMiss(HintSeg * botSeg,HintSeg * topSeg)160 HStemMiss(HintSeg* botSeg, HintSeg* topSeg)
161 {
162 Fixed brght, blft, bloc, tloc, trght, tlft;
163 Fixed mndist, dist, dy;
164 Fixed b, t, minDiff, minW, w;
165 int i;
166 if (gNumHStems == 0)
167 return;
168 brght = botSeg->sMax;
169 blft = botSeg->sMin;
170 trght = topSeg->sMax;
171 tlft = topSeg->sMin;
172 bloc = botSeg->sLoc;
173 tloc = topSeg->sLoc;
174 dy = abs(bloc - tloc);
175 if (dy < gMinDist)
176 return;
177 /* left is always < right */
178 if ((tlft <= brght) && (trght >= blft)) { /* overlap */
179 Fixed overlaplen = NUMMIN(trght, brght) - NUMMAX(tlft, blft);
180 Fixed minlen = NUMMIN(trght - tlft, brght - blft);
181 if (minlen == overlaplen)
182 dist = dy;
183 else
184 dist = CalcOverlapDist(dy, overlaplen, minlen);
185 } else
186 return;
187 mndist = FixTwoMul(gMinDist);
188 if (dist < mndist)
189 return;
190 minDiff = FixInt(1000);
191 minW = 0;
192 b = -bloc;
193 t = -tloc;
194 w = t - b;
195 /* don't check ghost bands for near misses */
196 if (((w = t - b) == botGhst) || (w == topGhst))
197 return;
198 w = abs(w);
199 for (i = 0; i < gNumHStems; i++) {
200 Fixed sw = gHStems[i];
201 Fixed diff = abs(sw - w);
202 if (diff == 0)
203 return;
204 if (diff < minDiff) {
205 minDiff = diff;
206 minW = sw;
207 }
208 }
209 if (minDiff > FixInt(2))
210 return;
211 ReportStemNearMiss(false, w, minW, b, t,
212 (botSeg->sType == sCURVE) || (topSeg->sType == sCURVE));
213 }
214
215 static void
EvalVPair(HintSeg * leftSeg,HintSeg * rightSeg,Fixed * pspc,Fixed * pv)216 EvalVPair(HintSeg* leftSeg, HintSeg* rightSeg, Fixed* pspc, Fixed* pv)
217 {
218 Fixed ltop, lbot, lloc, rloc, rtop, rbot;
219 Fixed mndist, dx, dist;
220 Fixed bonus, lbonus, rbonus;
221 *pspc = 0;
222 ltop = leftSeg->sMax;
223 lbot = leftSeg->sMin;
224 rtop = rightSeg->sMax;
225 rbot = rightSeg->sMin;
226 lloc = leftSeg->sLoc;
227 rloc = rightSeg->sLoc;
228 dx = abs(lloc - rloc);
229 if (dx < gMinDist) {
230 *pv = 0;
231 return;
232 }
233 /* top is always > bot, independent of YgoesUp */
234 if ((ltop >= rbot) && (lbot <= rtop)) { /* overlap */
235 Fixed overlaplen = NUMMIN(ltop, rtop) - NUMMAX(lbot, rbot);
236 Fixed minlen = NUMMIN(ltop - lbot, rtop - rbot);
237 if (minlen == overlaplen)
238 dist = dx;
239 else
240 dist = CalcOverlapDist(dx, overlaplen, minlen);
241 } else { /* no overlap; take closer ends */
242 Fixed tdst = abs(ltop - rbot);
243 Fixed bdst = abs(lbot - rtop);
244 Fixed dy = NUMMIN(tdst, bdst);
245 dist = (7 * dx) / 5 + GapDist(dy); /* extra penalty for nonoverlap */
246 DEBUG_ROUND(dist) /* DEBUG 8 BIT */
247 if (dy > dx)
248 dist *= dy / dx;
249 }
250 mndist = FixTwoMul(gMinDist);
251 dist = NUMMAX(dist, mndist);
252 lbonus = leftSeg->sBonus;
253 rbonus = rightSeg->sBonus;
254 bonus = NUMMIN(lbonus, rbonus);
255 *pspc = (bonus > 0) ? FixInt(2) : 0; /* this is for sol-eol characters */
256 if (gNumVStems > 0) {
257 int i;
258 Fixed w = abs(dx);
259 for (i = 0; i < gNumVStems; i++)
260 if (w == gVStems[i]) {
261 *pspc = *pspc + FixOne;
262 break;
263 }
264 }
265 AdjustVal(pv, ltop - lbot, rtop - rbot, dist, dx, false);
266 }
267
268 static void
VStemMiss(HintSeg * leftSeg,HintSeg * rightSeg)269 VStemMiss(HintSeg* leftSeg, HintSeg* rightSeg)
270 {
271 Fixed ltop, lbot, lloc, rloc, rtop, rbot;
272 Fixed dx, l, r, minDiff, minW, w;
273 int i;
274 if (gNumVStems == 0)
275 return;
276 ltop = leftSeg->sMax;
277 lbot = leftSeg->sMin;
278 rtop = rightSeg->sMax;
279 rbot = rightSeg->sMin;
280 lloc = leftSeg->sLoc;
281 rloc = rightSeg->sLoc;
282 dx = abs(lloc - rloc);
283 if (dx < gMinDist)
284 return;
285 /* top is always > bot, independent of YgoesUp */
286 if ((ltop < rbot) || (lbot > rtop)) /* does not overlap */
287 return;
288
289 l = lloc;
290 r = rloc;
291 w = abs(r - l);
292 minDiff = FixInt(1000);
293 minW = 0;
294 for (i = 0; i < gNumVStems; i++) {
295 Fixed sw = gVStems[i];
296 Fixed diff = abs(sw - w);
297 if (diff < minDiff) {
298 minDiff = diff;
299 minW = sw;
300 }
301 if (minDiff == 0)
302 return;
303 }
304 if (minDiff > FixInt(2))
305 return;
306 ReportStemNearMiss(true, w, minW, l, r,
307 (leftSeg->sType == sCURVE) ||
308 (rightSeg->sType == sCURVE));
309 }
310
311 static void
InsertVValue(Fixed lft,Fixed rght,Fixed val,Fixed spc,HintSeg * lSeg,HintSeg * rSeg)312 InsertVValue(Fixed lft, Fixed rght, Fixed val, Fixed spc, HintSeg* lSeg,
313 HintSeg* rSeg)
314 {
315 HintVal *item, *vlist, *vprev;
316 item = (HintVal*)Alloc(sizeof(HintVal));
317 item->vVal = val;
318 item->initVal = val;
319 item->vLoc1 = lft;
320 item->vLoc2 = rght;
321 item->vSpc = spc;
322 item->vSeg1 = lSeg;
323 item->vSeg2 = rSeg;
324 item->vGhst = false;
325 vlist = gValList;
326 vprev = NULL;
327 while (vlist != NULL) {
328 if (vlist->vLoc1 >= lft)
329 break;
330 vprev = vlist;
331 vlist = vlist->vNxt;
332 }
333 while (vlist != NULL && vlist->vLoc1 == lft) {
334 if (vlist->vLoc2 >= rght)
335 break;
336 vprev = vlist;
337 vlist = vlist->vNxt;
338 }
339 if (vprev == NULL)
340 gValList = item;
341 else
342 vprev->vNxt = item;
343 item->vNxt = vlist;
344 ReportAddVVal(item);
345 }
346
347 #define LePruneValue(val) ((val) < FixOne && ((val) << 10) <= gPruneValue)
348
349 static void
AddVValue(Fixed lft,Fixed rght,Fixed val,Fixed spc,HintSeg * lSeg,HintSeg * rSeg)350 AddVValue(Fixed lft, Fixed rght, Fixed val, Fixed spc, HintSeg* lSeg,
351 HintSeg* rSeg)
352 {
353 if (val == 0)
354 return;
355 if (LePruneValue(val) && spc <= 0)
356 return;
357 if (lSeg != NULL && lSeg->sType == sBEND && rSeg != NULL &&
358 rSeg->sType == sBEND)
359 return;
360 if (val <= gPruneD && spc <= 0 && lSeg != NULL && rSeg != NULL) {
361 if (lSeg->sType == sBEND || rSeg->sType == sBEND ||
362 !CheckBBoxes(lSeg->sElt, rSeg->sElt))
363 return;
364 }
365 if (rSeg == NULL)
366 return;
367 InsertVValue(lft, rght, val, spc, lSeg, rSeg);
368 }
369
370 static void
InsertHValue(Fixed bot,Fixed top,Fixed val,Fixed spc,HintSeg * bSeg,HintSeg * tSeg,bool ghst)371 InsertHValue(Fixed bot, Fixed top, Fixed val, Fixed spc, HintSeg* bSeg,
372 HintSeg* tSeg, bool ghst)
373 {
374 HintVal *item, *vlist, *vprev, *vl;
375 vlist = gValList;
376 vprev = NULL;
377 while (vlist != NULL) {
378 if (vlist->vLoc2 >= top)
379 break;
380 vprev = vlist;
381 vlist = vlist->vNxt;
382 }
383 while (vlist != NULL && vlist->vLoc2 == top) {
384 if (vlist->vLoc1 >= bot)
385 break;
386 vprev = vlist;
387 vlist = vlist->vNxt;
388 }
389 /* prune ghost pair that is same as non ghost pair for same segment
390 only if val for ghost is less than an existing val with same
391 top and bottom segment (vl) */
392 vl = vlist;
393 while (ghst && vl != NULL && vl->vLoc2 == top && vl->vLoc1 == bot) {
394 if (!vl->vGhst && (vl->vSeg1 == bSeg || vl->vSeg2 == tSeg) &&
395 vl->vVal > val)
396 return;
397 vl = vl->vNxt;
398 }
399 item = (HintVal*)Alloc(sizeof(HintVal));
400 item->vVal = val;
401 item->initVal = val;
402 item->vSpc = spc;
403 item->vLoc1 = bot;
404 item->vLoc2 = top;
405 item->vSeg1 = bSeg;
406 item->vSeg2 = tSeg;
407 item->vGhst = ghst;
408 if (vprev == NULL)
409 gValList = item;
410 else
411 vprev->vNxt = item;
412 item->vNxt = vlist;
413 ReportAddHVal(item);
414 }
415
416 static void
AddHValue(Fixed bot,Fixed top,Fixed val,Fixed spc,HintSeg * bSeg,HintSeg * tSeg)417 AddHValue(Fixed bot, Fixed top, Fixed val, Fixed spc, HintSeg* bSeg,
418 HintSeg* tSeg)
419 {
420 bool ghst;
421 if (val == 0)
422 return;
423 if (LePruneValue(val) && spc <= 0)
424 return;
425 if (bSeg->sType == sBEND && tSeg->sType == sBEND)
426 return;
427 ghst = bSeg->sType == sGHOST || tSeg->sType == sGHOST;
428 if (!ghst && val <= gPruneD && spc <= 0) {
429 if (bSeg->sType == sBEND || tSeg->sType == sBEND ||
430 !CheckBBoxes(bSeg->sElt, tSeg->sElt))
431 return;
432 }
433 InsertHValue(bot, top, val, spc, bSeg, tSeg, ghst);
434 }
435
436 static float
mfabs(float in)437 mfabs(float in)
438 {
439 if (in > 0)
440 return in;
441 return -in;
442 }
443
444 static Fixed
CombVals(Fixed v1,Fixed v2)445 CombVals(Fixed v1, Fixed v2)
446 {
447 int32_t i;
448 float r1, r2;
449 float x, a, xx = 0;
450 acfixtopflt(v1, &r1);
451 acfixtopflt(v2, &r2);
452 /* home brew sqrt */
453 a = r1 * r2;
454 x = a;
455 for (i = 0; i < 16; i++) {
456 xx = ((float)0.5) * (x + a / x);
457 if (i >= 8 && mfabs(xx - x) <= mfabs(xx) * 0.0000001f)
458 break;
459 x = xx;
460 }
461 r1 += r2 + ((float)2.0) * xx;
462 if (r1 > gMaxVal)
463 r1 = gMaxVal;
464 else if (r1 > 0 && r1 < gMinVal)
465 r1 = gMinVal;
466 return acpflttofix(&r1);
467 }
468
469 static void
CombineValues(void)470 CombineValues(void)
471 { /* works for both H and V */
472 HintVal* vlist = gValList;
473 while (vlist != NULL) {
474 HintVal* v1 = vlist->vNxt;
475 Fixed loc1 = vlist->vLoc1;
476 Fixed loc2 = vlist->vLoc2;
477 Fixed val = vlist->vVal;
478 bool match = false;
479 while (v1 != NULL && v1->vLoc1 == loc1 && v1->vLoc2 == loc2) {
480 if (v1->vGhst)
481 val = v1->vVal;
482 else
483 val = CombVals(val, v1->vVal);
484 /* increase value to compensate for length squared effect */
485 match = true;
486 v1 = v1->vNxt;
487 }
488 if (match) {
489 while (vlist != v1) {
490 vlist->vVal = val;
491 vlist = vlist->vNxt;
492 }
493 } else
494 vlist = v1;
495 }
496 }
497
498 void
EvalV(void)499 EvalV(void)
500 {
501 HintSeg *lList, *rList;
502 Fixed lft, rght;
503 Fixed val, spc;
504 gValList = NULL;
505 lList = leftList;
506 while (lList != NULL) {
507 rList = rightList;
508 while (rList != NULL) {
509 lft = lList->sLoc;
510 rght = rList->sLoc;
511 if (lft < rght) {
512 EvalVPair(lList, rList, &spc, &val);
513 VStemMiss(lList, rList);
514 AddVValue(lft, rght, val, spc, lList, rList);
515 }
516 rList = rList->sNxt;
517 }
518 lList = lList->sNxt;
519 }
520 CombineValues();
521 }
522
523 void
EvalH(void)524 EvalH(void)
525 {
526 HintSeg *bList, *tList, *lst, *ghostSeg;
527 Fixed lstLoc, tempLoc, cntr;
528 Fixed val, spc;
529 gValList = NULL;
530 bList = botList;
531 while (bList != NULL) {
532 tList = topList;
533 while (tList != NULL) {
534 Fixed bot, top;
535 bot = bList->sLoc;
536 top = tList->sLoc;
537 if (bot > top) {
538 EvalHPair(bList, tList, &spc, &val);
539 HStemMiss(bList, tList);
540 AddHValue(bot, top, val, spc, bList, tList);
541 }
542 tList = tList->sNxt;
543 }
544 bList = bList->sNxt;
545 }
546 ghostSeg = (HintSeg*)Alloc(sizeof(HintSeg));
547 ghostSeg->sType = sGHOST;
548 ghostSeg->sElt = NULL;
549 if (gLenBotBands < 2 && gLenTopBands < 2)
550 goto done;
551 lst = botList;
552 while (lst != NULL) {
553 lstLoc = lst->sLoc;
554 if (InBlueBand(lstLoc, gLenBotBands, gBotBands)) {
555 tempLoc = lstLoc - gGhostWidth;
556 ghostSeg->sLoc = tempLoc;
557 cntr = (lst->sMax + lst->sMin) / 2;
558 ghostSeg->sMax = cntr + gGhostLength / 2;
559 ghostSeg->sMin = cntr - gGhostLength / 2;
560 DEBUG_ROUND(ghostSeg->sMax) /* DEBUG 8 BIT */
561 DEBUG_ROUND(ghostSeg->sMin) /* DEBUG 8 BIT */
562 spc = FixInt(2);
563 val = FixInt(20);
564 AddHValue(lstLoc, tempLoc, val, spc, lst, ghostSeg);
565 }
566 lst = lst->sNxt;
567 }
568 lst = topList;
569 while (lst != NULL) {
570 lstLoc = lst->sLoc;
571 if (InBlueBand(lstLoc, gLenTopBands, gTopBands)) {
572 tempLoc = lstLoc + gGhostWidth;
573 ghostSeg->sLoc = tempLoc;
574 cntr = (lst->sMin + lst->sMax) / 2;
575 ghostSeg->sMax = cntr + gGhostLength / 2;
576 ghostSeg->sMin = cntr - gGhostLength / 2;
577 spc = FixInt(2);
578 val = FixInt(20);
579 AddHValue(tempLoc, lstLoc, val, spc, ghostSeg, lst);
580 }
581 lst = lst->sNxt;
582 }
583 done:
584 CombineValues();
585 }
586