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 
12 int32_t
CountSubPaths(void)13 CountSubPaths(void)
14 {
15     PathElt* e = gPathStart;
16     int32_t cnt = 0;
17     while (e != NULL) {
18         if (e->type == MOVETO)
19             cnt++;
20         e = e->next;
21     }
22     return cnt;
23 }
24 void
RoundPathCoords(void)25 RoundPathCoords(void)
26 {
27     PathElt* e;
28     e = gPathStart;
29     while (e != NULL) {
30         if (e->type == CURVETO) {
31             e->x1 = FHalfRnd(e->x1);
32             e->y1 = FHalfRnd(e->y1);
33             e->x2 = FHalfRnd(e->x2);
34             e->y2 = FHalfRnd(e->y2);
35             e->x3 = FHalfRnd(e->x3);
36             e->y3 = FHalfRnd(e->y3);
37         } else if (e->type == LINETO || e->type == MOVETO) {
38             e->x = FHalfRnd(e->x);
39             e->y = FHalfRnd(e->y);
40         }
41         e = e->next;
42     }
43 }
44 
45 static int32_t
CheckForHint(void)46 CheckForHint(void)
47 {
48     PathElt *mt, *cp;
49     mt = gPathStart;
50     while (mt != NULL) {
51         if (mt->type != MOVETO) {
52             ExpectedMoveTo(mt);
53             return -1;
54         }
55         cp = GetClosedBy(mt);
56         if (cp == NULL) {
57             ReportMissingClosePath();
58             return -1;
59         }
60         mt = cp->next;
61     }
62     return 0;
63 }
64 
65 bool
PreCheckForHinting(void)66 PreCheckForHinting(void)
67 {
68     PathElt* e;
69     int32_t cnt = 0;
70     while (gPathEnd != NULL) {
71         if (gPathEnd->type == MOVETO)
72             Delete(gPathEnd);
73         else if (gPathEnd->type != CLOSEPATH) {
74             ReportMissingClosePath();
75             return false;
76         } else
77             break;
78     }
79     e = gPathStart;
80     while (e != NULL) {
81         if (e->type == CLOSEPATH) {
82             PathElt* nxt;
83             if (e == gPathEnd)
84                 break;
85             nxt = e->next;
86             if (nxt->type == MOVETO) {
87                 e = nxt;
88                 continue;
89             }
90             if (nxt->type == CLOSEPATH) { /* remove double closepath */
91                 Delete(nxt);
92                 continue;
93             }
94         }
95         e = e->next;
96     }
97     while (true) {
98         int32_t chk = CheckForHint();
99         if (chk == -1)
100             return false;
101         if (chk == 0)
102             break;
103         if (++cnt > 10) {
104             LogMsg(WARNING, OK, "Looping in PreCheckForHints!.");
105             break;
106         }
107     }
108     return true;
109 }
110 
111 static PathElt*
GetSubpathNext(PathElt * e)112 GetSubpathNext(PathElt* e)
113 {
114     while (true) {
115         e = e->next;
116         if (e == NULL)
117             break;
118         if (e->type == CLOSEPATH)
119             break;
120         if (!IsTiny(e))
121             break;
122     }
123     return e;
124 }
125 
126 static PathElt*
GetSubpathPrev(PathElt * e)127 GetSubpathPrev(PathElt* e)
128 {
129     while (true) {
130         e = e->prev;
131         if (e == NULL)
132             break;
133         if (e->type == MOVETO)
134             e = GetClosedBy(e);
135         if (!IsTiny(e))
136             break;
137     }
138     return e;
139 }
140 
141 static bool
AddAutoFlexProp(PathElt * e,bool yflag)142 AddAutoFlexProp(PathElt* e, bool yflag)
143 {
144     PathElt *e0 = e, *e1 = e->next;
145     if (e0->type != CURVETO || e1->type != CURVETO) {
146         LogMsg(LOGERROR, NONFATALERROR, "Illegal input.");
147     }
148     /* Don't add flex to linear curves. */
149     if (yflag && e0->y3 == e1->y1 && e1->y1 == e1->y2 && e1->y2 == e1->y3)
150         return false;
151     else if (e0->x3 == e1->x1 && e1->x1 == e1->x2 && e1->x2 == e1->x3)
152         return false;
153     e0->yFlex = yflag;
154     e1->yFlex = yflag;
155     e0->isFlex = true;
156     e1->isFlex = true;
157     return true;
158 }
159 
160 #define LENGTHRATIOCUTOFF                                                      \
161     0.11 /* 0.33^2 : two curves must be in approximate length ratio of 1:3 or  \
162             better */
163 
164 static void
TryYFlex(PathElt * e,PathElt * n,Fixed x0,Fixed y0,Fixed x1,Fixed y1)165 TryYFlex(PathElt* e, PathElt* n, Fixed x0, Fixed y0, Fixed x1, Fixed y1)
166 {
167     Fixed x2, y2, x3, y3, x4, y4;
168     double d0sq, d1sq, quot, dx, dy;
169 
170     GetEndPoint(n, &x2, &y2);
171     dy = abs(y0 - y2);
172     if (dy > gFlexCand)
173         return; /* too big diff in bases. If dy is within flexCand, flex will
174                    fail , but we will report it as a candidate. */
175     dx = abs(x0 - x2);
176     if (dx < MAXFLEX)
177         return; /* Let's not add flex to features less than MAXFLEX wide. */
178     if (dx < (3 * abs(y0 - y2)))
179         return; /* We want the width to be at least three times the height. */
180     if (ProdLt0(y1 - y0, y1 - y2))
181         return; /* y0 and y2 not on same side of y1 */
182 
183     /* check the ratios of the "lengths" of 'e' and 'n'  */
184     dx = (x1 - x0);
185     dy = (y1 - y0);
186     d0sq = dx * dx + dy * dy;
187     dx = (x2 - x1);
188     dy = (y2 - y1);
189     d1sq = dx * dx + dy * dy;
190     quot = (d0sq > d1sq) ? (d1sq / d0sq) : (d0sq / d1sq);
191     if (quot < LENGTHRATIOCUTOFF)
192         return;
193 
194     if (gFlexStrict) {
195         bool top, dwn;
196         PathElt *p, *q;
197         q = GetSubpathNext(n);
198         GetEndPoint(q, &x3, &y3);
199         if (ProdLt0(y3 - y2, y1 - y2))
200             return; /* y1 and y3 not on same side of y2 */
201         p = GetSubpathPrev(e);
202         GetEndPoint(p->prev, &x4, &y4);
203         if (ProdLt0(y4 - y0, y1 - y0))
204             return; /* y1 and y4 not on same side of y0 */
205         top = (x0 > x1) ? true : false;
206         dwn = (y1 > y0) ? true : false;
207         if ((top && !dwn) || (!top && dwn))
208             return; /* concave */
209     }
210     if (n != e->next) { /* something in the way */
211         n = e->next;
212         ReportTryFlexError(n->type == CLOSEPATH, x1, y1);
213         return;
214     }
215     if (y0 != y2) {
216         ReportTryFlexNearMiss(x0, y0, x2, y2);
217         return;
218     }
219     if (AddAutoFlexProp(e, true))
220         ReportAddFlex();
221 }
222 
223 static void
TryXFlex(PathElt * e,PathElt * n,Fixed x0,Fixed y0,Fixed x1,Fixed y1)224 TryXFlex(PathElt* e, PathElt* n, Fixed x0, Fixed y0, Fixed x1, Fixed y1)
225 {
226     Fixed x2, y2, x3, y3, x4, y4;
227     double d0sq, d1sq, quot, dx, dy;
228 
229     GetEndPoint(n, &x2, &y2);
230     dx = abs(y0 - y2);
231     if (dx > gFlexCand)
232         return; /* too big diff in bases */
233 
234     dy = abs(x0 - x2);
235     if (dy < MAXFLEX)
236         return; /* Let's not add flex to features less than MAXFLEX wide. */
237     if (dy < (3 * abs(x0 - x2)))
238         return; /* We want the width to be at least three times the height. */
239 
240     if (ProdLt0(x1 - x0, x1 - x2))
241         return; /* x0 and x2 not on same side of x1 */
242 
243     /* check the ratios of the "lengths" of 'e' and 'n'  */
244     dx = (x1 - x0);
245     dy = (y1 - y0);
246     d0sq = dx * dx + dy * dy;
247     dx = (x2 - x1);
248     dy = (y2 - y1);
249     d1sq = dx * dx + dy * dy;
250     quot = (d0sq > d1sq) ? (d1sq / d0sq) : (d0sq / d1sq);
251     if (quot < LENGTHRATIOCUTOFF)
252         return;
253 
254     if (gFlexStrict) {
255         PathElt *p, *q;
256         bool lft;
257         q = GetSubpathNext(n);
258         GetEndPoint(q, &x3, &y3);
259         if (ProdLt0(x3 - x2, x1 - x2))
260             return; /* x1 and x3 not on same side of x2 */
261         p = GetSubpathPrev(e);
262         GetEndPoint(p->prev, &x4, &y4);
263         if (ProdLt0(x4 - x0, x1 - x0))
264             return; /* x1 and x4 not on same side of x0 */
265         lft = (y0 < y2) ? true : false;
266         if ((lft && x0 > x1) || (!lft && x0 < x1))
267             return; /* concave */
268     }
269     if (n != e->next) { /* something in the way */
270         n = e->next;
271         ReportTryFlexError(n->type == CLOSEPATH, x1, y1);
272         return;
273     }
274     if (x0 != x2) {
275         ReportTryFlexNearMiss(x0, y0, x2, y2);
276         return;
277     }
278     if (AddAutoFlexProp(e, false))
279         ReportAddFlex();
280 }
281 
282 void
AutoAddFlex(void)283 AutoAddFlex(void)
284 {
285     PathElt *e, *n;
286     Fixed x0, y0, x1, y1;
287     e = gPathStart;
288     while (e != NULL) {
289         if (e->type != CURVETO || e->isFlex)
290             goto Nxt;
291         n = GetSubpathNext(e);
292         if (n->type != CURVETO)
293             goto Nxt;
294         GetEndPoints(e, &x0, &y0, &x1, &y1);
295         if (abs(y0 - y1) <= MAXFLEX)
296             TryYFlex(e, n, x0, y0, x1, y1);
297         if (abs(x0 - x1) <= MAXFLEX)
298             TryXFlex(e, n, x0, y0, x1, y1);
299     Nxt:
300         e = e->next;
301     }
302 }
303