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 <math.h>
11
12 #include "ac.h"
13
14 static bool g_xflat, g_yflat, g_xdone, g_ydone, g_bbquit;
15 static int32_t g_xstate, g_ystate, g_xstart, g_ystart;
16 static Fixed g_x0, g_cy0, g_x1, g_cy1, g_xloc, g_yloc;
17 static Fixed g_x, g_y, g_xnxt, g_ynxt;
18 static Fixed g_yflatstartx, g_yflatstarty, g_yflatendx, g_yflatendy;
19 static Fixed g_xflatstarty, g_xflatstartx, g_xflatendx, g_xflatendy;
20 static bool g_vert, g_started, g_reCheckSmooth;
21 static Fixed g_loc, g_frst, g_lst, g_fltnvalue;
22 static PathElt* g_e;
23 static bool g_forMultiMaster = false, g_inflPtFound = false;
24
25 #define STARTING (0)
26 #define goingUP (1)
27 #define goingDOWN (2)
28
29 /* DEBUG 8 BIT. The SDELTA value must tbe increased by 2 due to change in
30 * coordinate system from 7 to 8 bit FIXED fraction. */
31
32 #define SDELTA (FixInt(8))
33 #define SDELTA3 (FixInt(10))
34
35 static void
chkBad(void)36 chkBad(void)
37 {
38 g_reCheckSmooth = ResolveConflictBySplit(g_e, false, NULL, NULL);
39 ;
40 }
41
42 #define GrTan(n, d) (abs(n) * 100 > abs(d) * gSCurveTan)
43 #define LsTan(n, d) (abs(n) * 100 < abs(d) * gSCurveTan)
44
45 static void
chkYDIR(void)46 chkYDIR(void)
47 {
48 if (g_y > g_yloc) { /* going up */
49 if (g_ystate == goingUP)
50 return;
51 if (g_ystate == STARTING)
52 g_ystart = g_ystate = goingUP;
53 else /*if (ystate == goingDOWN)*/ {
54 if (g_ystart == goingUP) {
55 g_yflatendx = g_xloc;
56 g_yflatendy = g_yloc;
57 } else if (!g_yflat) {
58 g_yflatstartx = g_xloc;
59 g_yflatstarty = g_yloc;
60 g_yflat = true;
61 }
62 g_ystate = goingUP;
63 }
64 } else if (g_y < g_yloc) { /* going down */
65 if (g_ystate == goingDOWN)
66 return;
67 if (g_ystate == STARTING)
68 g_ystart = g_ystate = goingDOWN;
69 else /*if (ystate == goingUP)*/ {
70 if (g_ystart == goingDOWN) {
71 g_yflatendx = g_xloc;
72 g_yflatendy = g_yloc;
73 } else if (!g_yflat) {
74 g_yflatstartx = g_xloc;
75 g_yflatstarty = g_yloc;
76 g_yflat = true;
77 }
78 g_ystate = goingDOWN;
79 }
80 }
81 }
82
83 static void
chkYFLAT(void)84 chkYFLAT(void)
85 {
86 if (!g_yflat) {
87 if (LsTan(g_y - g_yloc, g_x - g_xloc)) {
88 g_yflat = true;
89 g_yflatstartx = g_xloc;
90 g_yflatstarty = g_yloc;
91 }
92 return;
93 }
94 if (g_ystate != g_ystart)
95 return;
96 if (GrTan(g_y - g_yloc, g_x - g_xloc)) {
97 g_yflatendx = g_xloc;
98 g_yflatendy = g_yloc;
99 g_ydone = true;
100 }
101 }
102
103 static void
chkXFLAT(void)104 chkXFLAT(void)
105 {
106 if (!g_xflat) {
107 if (LsTan(g_x - g_xloc, g_y - g_yloc)) {
108 g_xflat = true;
109 g_xflatstartx = g_xloc;
110 g_xflatstarty = g_yloc;
111 }
112 return;
113 }
114 if (g_xstate != g_xstart)
115 return;
116 if (GrTan(g_x - g_xloc, g_y - g_yloc)) {
117 g_xflatendx = g_xloc;
118 g_xflatendy = g_yloc;
119 g_xdone = true;
120 }
121 }
122
123 static void
chkXDIR(void)124 chkXDIR(void)
125 {
126 if (g_x > g_xloc) { /* going up */
127 if (g_xstate == goingUP)
128 return;
129 if (g_xstate == STARTING)
130 g_xstart = g_xstate = goingUP;
131 else /*if (xstate == goingDOWN)*/ {
132 if (g_xstart == goingUP) {
133 g_xflatendx = g_xloc;
134 g_xflatendy = g_yloc;
135 } else if (!g_xflat) {
136 g_xflatstartx = g_xloc;
137 g_xflatstarty = g_yloc;
138 g_xflat = true;
139 }
140 g_xstate = goingUP;
141 }
142 } else if (g_x < g_xloc) {
143 if (g_xstate == goingDOWN)
144 return;
145 if (g_xstate == STARTING)
146 g_xstart = g_xstate = goingDOWN;
147 else /*if (xstate == goingUP)*/ {
148 if (g_xstart == goingDOWN) {
149 g_xflatendx = g_xloc;
150 g_xflatendy = g_yloc;
151 } else if (!g_xflat) {
152 g_xflatstartx = g_xloc;
153 g_xflatstarty = g_yloc;
154 g_xflat = true;
155 }
156 g_xstate = goingDOWN;
157 }
158 }
159 }
160
161 static void
chkDT(Cd c)162 chkDT(Cd c)
163 {
164 Fixed loc;
165
166 g_x = c.x;
167 g_y = c.y;
168 g_ynxt = g_y;
169 g_xnxt = g_x;
170 if (!g_ydone) {
171 chkYDIR();
172 chkYFLAT();
173 if (g_ydone && g_yflat && abs(g_yflatstarty - g_cy0) > SDELTA &&
174 abs(g_cy1 - g_yflatendy) > SDELTA) {
175 if ((g_ystart == goingUP && g_yflatstarty - g_yflatendy > SDELTA) ||
176 (g_ystart == goingDOWN && g_yflatendy - g_yflatstarty > SDELTA)) {
177 if (gEditGlyph && !g_forMultiMaster)
178 chkBad();
179 return;
180 }
181 if (abs(g_yflatstartx - g_yflatendx) > SDELTA3) {
182 DEBUG_ROUND(g_yflatstartx);
183 DEBUG_ROUND(g_yflatendx);
184 DEBUG_ROUND(g_yflatstarty);
185 DEBUG_ROUND(g_yflatendy);
186
187 loc = (g_yflatstarty + g_yflatendy) / 2;
188 DEBUG_ROUND(loc);
189
190 if (!g_forMultiMaster) {
191 AddHSegment(g_yflatstartx, g_yflatendx, loc, g_e, NULL, sCURVE,
192 13);
193 } else {
194 g_inflPtFound = true;
195 g_fltnvalue = -loc;
196 }
197 }
198 }
199 }
200 if (!g_xdone) {
201 chkXDIR();
202 chkXFLAT();
203 if (g_xdone && g_xflat && abs(g_xflatstartx - g_x0) > SDELTA &&
204 abs(g_x1 - g_xflatendx) > SDELTA) {
205 if ((g_xstart == goingUP && g_xflatstartx - g_xflatendx > SDELTA) ||
206 (g_xstart == goingDOWN && g_xflatendx - g_xflatstartx > SDELTA)) {
207 if (gEditGlyph && !g_forMultiMaster)
208 chkBad();
209 return;
210 }
211 if (abs(g_xflatstarty - g_xflatendy) > SDELTA3) {
212 DEBUG_ROUND(g_xflatstarty);
213 DEBUG_ROUND(g_xflatendy);
214 DEBUG_ROUND(g_xflatstartx);
215 DEBUG_ROUND(g_xflatendx);
216
217 loc = (g_xflatstartx + g_xflatendx) / 2;
218 DEBUG_ROUND(loc);
219
220 if (!g_forMultiMaster)
221
222 {
223 AddVSegment(g_xflatstarty, g_xflatendy, loc, g_e, NULL, sCURVE,
224 13);
225 } else {
226 g_inflPtFound = true;
227 g_fltnvalue = loc;
228 }
229 }
230 }
231 }
232 g_xloc = g_xnxt;
233 g_yloc = g_ynxt;
234 }
235
236 #define FQ(x) ((int32_t)((x) >> 6))
237 static int32_t
CPDirection(Fixed x1,Fixed cy1,Fixed x2,Fixed y2,Fixed x3,Fixed y3)238 CPDirection(Fixed x1, Fixed cy1, Fixed x2, Fixed y2, Fixed x3, Fixed y3)
239 {
240 int32_t q, q1, q2, q3;
241 q1 = FQ(x2) * FQ(y3 - cy1);
242 q2 = FQ(x1) * FQ(y2 - y3);
243 q3 = FQ(x3) * FQ(cy1 - y2);
244 q = q1 + q2 + q3;
245 if (q > 0)
246 return 1;
247 if (q < 0)
248 return -1;
249 return 0;
250 }
251
252 void
RMovePoint(Fixed dx,Fixed dy,int32_t whichcp,PathElt * e)253 RMovePoint(Fixed dx, Fixed dy, int32_t whichcp, PathElt* e)
254 {
255 if (whichcp == cpStart) {
256 e = e->prev;
257 whichcp = cpEnd;
258 }
259 if (whichcp == cpEnd) {
260 if (e->type == CLOSEPATH)
261 e = GetDest(e);
262 if (e->type == CURVETO) {
263 e->x3 += dx;
264 e->y3 += dy;
265 } else {
266 e->x += dx;
267 e->y += dy;
268 }
269 return;
270 }
271 if (whichcp == cpCurve1) {
272 e->x1 += dx;
273 e->y1 += dy;
274 return;
275 }
276 if (whichcp == cpCurve2) {
277 e->x2 += dx;
278 e->y2 += dy;
279 return;
280 }
281 LogMsg(LOGERROR, NONFATALERROR, "Malformed path list.");
282 }
283
284 void
Delete(PathElt * e)285 Delete(PathElt* e)
286 {
287 PathElt *nxt, *prv;
288 nxt = e->next;
289 prv = e->prev;
290 if (nxt != NULL)
291 nxt->prev = prv;
292 else
293 gPathEnd = prv;
294 if (prv != NULL)
295 prv->next = nxt;
296 else
297 gPathStart = nxt;
298 }
299
300 /* This procedure is called from BuildFont when adding hints
301 to base designs of a multi-master font. */
302 bool
GetInflectionPoint(Fixed px,Fixed py,Fixed px1,Fixed pcy1,Fixed px2,Fixed py2,Fixed px3,Fixed py3,Fixed * inflPt)303 GetInflectionPoint(Fixed px, Fixed py, Fixed px1, Fixed pcy1, Fixed px2,
304 Fixed py2, Fixed px3, Fixed py3, Fixed* inflPt)
305 {
306 FltnRec fltnrec;
307 Cd c0, c1, c2, c3;
308
309 fltnrec.report = chkDT;
310 c0.x = px;
311 c0.y = -py;
312 c1.x = px1;
313 c1.y = -pcy1;
314 c2.x = px2;
315 c2.y = -py2;
316 c3.x = px3;
317 c3.y = -py3;
318 g_xstate = g_ystate = STARTING;
319 g_xdone = g_ydone = g_xflat = g_yflat = g_inflPtFound = false;
320 g_x0 = c0.x;
321 g_cy0 = c0.y;
322 g_x1 = c3.x;
323 g_cy1 = c3.y;
324 g_xloc = g_x0;
325 g_yloc = g_cy0;
326 g_forMultiMaster = true;
327 FltnCurve(c0, c1, c2, c3, &fltnrec);
328 if (g_inflPtFound)
329 *inflPt = g_fltnvalue;
330 return g_inflPtFound;
331 }
332
333 static void
CheckSCurve(PathElt * ee)334 CheckSCurve(PathElt* ee)
335 {
336 FltnRec fr;
337 Cd c0, c1, c2, c3;
338 if (ee->type != CURVETO) {
339 LogMsg(LOGERROR, NONFATALERROR, "Malformed path list.");
340 }
341
342 GetEndPoint(ee->prev, &c0.x, &c0.y);
343 fr.report = chkDT;
344 c1.x = ee->x1;
345 c1.y = ee->y1;
346 c2.x = ee->x2;
347 c2.y = ee->y2;
348 c3.x = ee->x3;
349 c3.y = ee->y3;
350 g_xstate = g_ystate = STARTING;
351 g_xdone = g_ydone = g_xflat = g_yflat = false;
352 g_x0 = c0.x;
353 g_cy0 = c0.y;
354 g_x1 = c3.x;
355 g_cy1 = c3.y;
356 g_xloc = g_x0;
357 g_yloc = g_cy0;
358 g_e = ee;
359 g_forMultiMaster = false;
360 FltnCurve(c0, c1, c2, c3, &fr);
361 }
362
363 static void
CheckZeroLength(void)364 CheckZeroLength(void)
365 {
366 PathElt *e, *NxtE;
367 Fixed x0, cy0, x1, cy1, x2, y2, x3, y3;
368 if ((!gEditGlyph) || g_forMultiMaster)
369 {
370 /* Do not change topology when hinting MM fonts,
371 and do not edit glyphs if not requested */
372 return;
373 }
374 e = gPathStart;
375 while (e != NULL) { /* delete zero length elements */
376 NxtE = e->next;
377 GetEndPoints(e, &x0, &cy0, &x1, &cy1);
378 if (e->type == LINETO && x0 == x1 && cy0 == cy1) {
379 Delete(e);
380 goto Nxt1;
381 }
382 if (e->type == CURVETO) {
383 x2 = e->x1;
384 y2 = e->y1;
385 x3 = e->x2;
386 y3 = e->y2;
387 if (x0 == x1 && cy0 == cy1 && x2 == x1 && x3 == x1 && y2 == cy1 &&
388 y3 == cy1) {
389 Delete(e);
390 goto Nxt1;
391 }
392 }
393 Nxt1:
394 e = NxtE;
395 }
396 }
397
398 void
CheckSmooth(void)399 CheckSmooth(void)
400 {
401 PathElt *e, *nxt, *NxtE;
402 bool recheck;
403 Fixed x0, cy0, x1, cy1, x2, y2, x3, y3, smdiff, xx, yy;
404 CheckZeroLength();
405 restart:
406 g_reCheckSmooth = false;
407 recheck = false;
408 e = gPathStart;
409 while (e != NULL) {
410 NxtE = e->next;
411 if (e->type == MOVETO || IsTiny(e) || e->isFlex)
412 goto Nxt;
413 GetEndPoint(e, &x1, &cy1);
414 if (e->type == CURVETO) {
415 int32_t cpd0, cpd1;
416 x2 = e->x1;
417 y2 = e->y1;
418 x3 = e->x2;
419 y3 = e->y2;
420 GetEndPoint(e->prev, &x0, &cy0);
421 cpd0 = CPDirection(x0, cy0, x2, y2, x3, y3);
422 cpd1 = CPDirection(x2, y2, x3, y3, x1, cy1);
423 if (ProdLt0(cpd0, cpd1))
424 CheckSCurve(e);
425 }
426 nxt = NxtForBend(e, &x2, &y2, &xx, &yy);
427 if (nxt->isFlex)
428 goto Nxt;
429 PrvForBend(nxt, &x0, &cy0);
430 if (!CheckSmoothness(x0, cy0, x1, cy1, x2, y2, &smdiff))
431 LogMsg(INFO, OK, "Junction at %g %g may need smoothing.",
432 FixToDbl(x1), FixToDbl(-cy1));
433 if (smdiff > FixInt(160))
434 LogMsg(INFO, OK, "Too sharp angle at %g %g has been clipped.",
435 FixToDbl(x1), FixToDbl(-cy1));
436 Nxt:
437 e = NxtE;
438 }
439 if (g_reCheckSmooth)
440 goto restart;
441 if (!recheck)
442 return;
443 CheckZeroLength();
444 /* in certain cases clip sharp point can produce a zero length line */
445 }
446
447 #define BBdist \
448 (FixInt(20)) /* DEBUG 8 BIT. DOuble value from 10 to 20 for change in \
449 coordinate system. */
450
451 static void
chkBBDT(Cd c)452 chkBBDT(Cd c)
453 {
454 Fixed x = c.x, y = c.y;
455 if (g_bbquit)
456 return;
457 if (g_vert) {
458 g_lst = y;
459 if (!g_started && abs(x - g_loc) <= BBdist) {
460 g_started = true;
461 g_frst = y;
462 } else if (g_started && abs(x - g_loc) > BBdist)
463 g_bbquit = true;
464 } else {
465 g_lst = x;
466 if (!g_started && abs(y - g_loc) <= BBdist) {
467 g_started = true;
468 g_frst = x;
469 } else if (g_started && abs(y - g_loc) > BBdist)
470 g_bbquit = true;
471 }
472 }
473
474 void
CheckForMultiMoveTo(void)475 CheckForMultiMoveTo(void)
476 {
477 PathElt* e = gPathStart;
478 bool moveto;
479 moveto = false;
480 while (e != NULL) {
481 if (e->type != MOVETO)
482 moveto = false;
483 else if (!moveto)
484 moveto = true;
485 else
486 Delete(e->prev); /* delete previous moveto */
487 e = e->next;
488 }
489 }
490
491 void
CheckBBoxEdge(PathElt * e,bool vrt,Fixed lc,Fixed * pf,Fixed * pl)492 CheckBBoxEdge(PathElt* e, bool vrt, Fixed lc, Fixed* pf, Fixed* pl)
493 {
494 FltnRec fr;
495 Cd c0, c1, c2, c3;
496 if (e->type != CURVETO) {
497 LogMsg(LOGERROR, NONFATALERROR, "Malformed path list.");
498 }
499
500 GetEndPoint(e->prev, &c0.x, &c0.y);
501 fr.report = chkBBDT;
502 g_bbquit = false;
503 c1.x = e->x1;
504 c1.y = e->y1;
505 c2.x = e->x2;
506 c2.y = e->y2;
507 c3.x = e->x3;
508 c3.y = e->y3;
509 g_loc = lc;
510 g_vert = vrt;
511 g_started = false;
512 chkBBDT(c0);
513 FltnCurve(c0, c1, c2, c3, &fr);
514 *pf = g_frst;
515 *pl = g_lst;
516 }
517
518 static void
MakeColinear(Fixed tx,Fixed ty,Fixed x0,Fixed cy0,Fixed x1,Fixed cy1,Fixed * xptr,Fixed * yptr)519 MakeColinear(Fixed tx, Fixed ty, Fixed x0, Fixed cy0, Fixed x1, Fixed cy1,
520 Fixed* xptr, Fixed* yptr)
521 {
522 Fixed dx, dy;
523 float rdx, rdy, dxdy, dxsq, dysq, dsq, xi, yi, rx, ry, rx0, ry0;
524 dx = x1 - x0;
525 dy = cy1 - cy0;
526 if (dx == 0 && dy == 0) {
527 *xptr = tx;
528 *yptr = ty;
529 return;
530 }
531 if (dx == 0) {
532 *xptr = x0;
533 *yptr = ty;
534 return;
535 }
536 if (dy == 0) {
537 *xptr = tx;
538 *yptr = cy0;
539 return;
540 }
541 acfixtopflt(dx, &rdx);
542 acfixtopflt(dy, &rdy);
543 acfixtopflt(x0, &rx0);
544 acfixtopflt(cy0, &ry0);
545 acfixtopflt(tx, &rx);
546 acfixtopflt(ty, &ry);
547 dxdy = rdx * rdy;
548 dxsq = rdx * rdx;
549 dysq = rdy * rdy;
550 dsq = dxsq + dysq;
551 xi = (rx * dxsq + rx0 * dysq + (ry - ry0) * dxdy) / dsq;
552 yi = ry0 + ((xi - rx0) * rdy) / rdx;
553 *xptr = acpflttofix(&xi);
554 *yptr = acpflttofix(&yi);
555 }
556
557 #define DEG(x) ((x)*57.29577951308232088)
558 static Fixed
ATan(Fixed a,Fixed b)559 ATan(Fixed a, Fixed b)
560 {
561 float aa, bb, cc;
562 acfixtopflt(a, &aa);
563 acfixtopflt(b, &bb);
564 cc = (float)DEG(atan2((double)aa, (double)bb));
565 while (cc < 0)
566 cc += 360.0f;
567 return acpflttofix(&cc);
568 }
569
570 bool
CheckSmoothness(Fixed x0,Fixed cy0,Fixed x1,Fixed cy1,Fixed x2,Fixed y2,Fixed * pd)571 CheckSmoothness(Fixed x0, Fixed cy0, Fixed x1, Fixed cy1, Fixed x2, Fixed y2,
572 Fixed* pd)
573 {
574 Fixed dx, dy, smdiff, smx, smy, at0, at1;
575 dx = x0 - x1;
576 dy = cy0 - cy1;
577 *pd = 0;
578 if (dx == 0 && dy == 0)
579 return true;
580 at0 = ATan(dx, dy);
581 dx = x1 - x2;
582 dy = cy1 - y2;
583 if (dx == 0 && dy == 0)
584 return true;
585 at1 = ATan(dx, dy);
586 smdiff = at0 - at1;
587 if (smdiff < 0)
588 smdiff = -smdiff;
589 if (smdiff >= FixInt(180))
590 smdiff = FixInt(360) - smdiff;
591 *pd = smdiff;
592 if (smdiff == 0 || smdiff > FixInt(30))
593 return true;
594 MakeColinear(x1, cy1, x0, cy0, x2, y2, &smx, &smy);
595 smx = FHalfRnd(smx);
596 smy = FHalfRnd(smy);
597 /* DEBUG 8 BIT. Double hard coded distance values, for change from 7 to 8
598 * bits for fractions. */
599 return abs(smx - x1) < FixInt(4) && abs(smy - cy1) < FixInt(4);
600 }
601
602 void
CheckForDups(void)603 CheckForDups(void)
604 {
605 PathElt *ob, *nxt;
606 Fixed x, y;
607 ob = gPathStart;
608 while (ob != NULL) {
609 nxt = ob->next;
610 if (ob->type == MOVETO) {
611 x = ob->x;
612 y = ob->y;
613 ob = nxt;
614 while (ob != NULL) {
615 if (ob->type == MOVETO && x == ob->x && y == ob->y)
616 goto foundMatch;
617 ob = ob->next;
618 }
619 }
620 ob = nxt;
621 }
622 return;
623 foundMatch:
624 y = -y;
625 ReportDuplicates(x, y);
626 }
627
628 void
MoveSubpathToEnd(PathElt * e)629 MoveSubpathToEnd(PathElt* e)
630 {
631 PathElt *subEnd, *subStart, *subNext, *subPrev;
632 subEnd = (e->type == CLOSEPATH) ? e : GetClosedBy(e);
633 subStart = GetDest(subEnd);
634 if (subEnd == gPathEnd)
635 return; /* already at end */
636 subNext = subEnd->next;
637 if (subStart == gPathStart) {
638 gPathStart = subNext;
639 subNext->prev = NULL;
640 } else {
641 subPrev = subStart->prev;
642 subPrev->next = subNext;
643 subNext->prev = subPrev;
644 }
645 gPathEnd->next = subStart;
646 subStart->prev = gPathEnd;
647 subEnd->next = NULL;
648 gPathEnd = subEnd;
649 }
650