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 PathElt*
GetDest(PathElt * cldest)13 GetDest(PathElt* cldest)
14 {
15     if (cldest == NULL)
16         return NULL;
17     while (true) {
18         cldest = cldest->prev;
19         if (cldest == NULL)
20             return gPathStart;
21         if (cldest->type == MOVETO)
22             return cldest;
23     }
24 }
25 
26 PathElt*
GetClosedBy(PathElt * clsdby)27 GetClosedBy(PathElt* clsdby)
28 {
29     if (clsdby == NULL)
30         return NULL;
31     if (clsdby->type == CLOSEPATH)
32         return clsdby;
33     while (true) {
34         clsdby = clsdby->next;
35         if (clsdby == NULL)
36             return NULL;
37         if (clsdby->type == MOVETO)
38             return NULL;
39         if (clsdby->type == CLOSEPATH)
40             return clsdby;
41     }
42 }
43 
44 void
GetEndPoint(PathElt * e,Fixed * x1p,Fixed * y1p)45 GetEndPoint(PathElt* e, Fixed* x1p, Fixed* y1p)
46 {
47 retry:
48     if (e == NULL) {
49         *x1p = 0;
50         *y1p = 0;
51         return;
52     }
53     switch (e->type) {
54         case MOVETO:
55         case LINETO:
56             *x1p = e->x;
57             *y1p = e->y;
58             break;
59         case CURVETO:
60             *x1p = e->x3;
61             *y1p = e->y3;
62             break;
63         case CLOSEPATH:
64             e = GetDest(e);
65             if (e == NULL || e->type == CLOSEPATH) {
66                 LogMsg(LOGERROR, NONFATALERROR, "Bad description.");
67             }
68             goto retry;
69         default: {
70             LogMsg(LOGERROR, NONFATALERROR, "Illegal operator.");
71         }
72     }
73 }
74 
75 void
GetEndPoints(PathElt * p,Fixed * px0,Fixed * py0,Fixed * px1,Fixed * py1)76 GetEndPoints(PathElt* p, Fixed* px0, Fixed* py0, Fixed* px1, Fixed* py1)
77 {
78     GetEndPoint(p, px1, py1);
79     GetEndPoint(p->prev, px0, py0);
80 }
81 
82 #define Interpolate(q, v0, q0, v1, q1) (v0 + (q - q0) * ((v1 - v0) / (q1 - q0)))
83 static Fixed
HVness(float * pq)84 HVness(float* pq)
85 {
86     float q;
87     float result;
88     /* approximately == 2 q neg exp */
89     /* as q -> 0, result goes to 1.0 */
90     /* as q -> inf, result goes to 0.0 */
91     q = *pq;
92     if (q < .25f)
93         result = (float)Interpolate(q, 1.0f, 0.0f, .841f, .25f);
94     else if (q < .5f)
95         result = (float)Interpolate(q, .841f, .25f, .707f, .5f);
96     else if (q < 1)
97         result = (float)Interpolate(q, .707f, .5f, .5f, 1.0f);
98     else if (q < 2)
99         result = (float)Interpolate(q, .5f, 1.0f, .25f, 2.0f);
100     else if (q < 4)
101         result = (float)Interpolate(q, .25f, 2.0f, 0.0f, 4.0f);
102     else
103         result = 0.0;
104     return acpflttofix(&result);
105 }
106 
107 Fixed
VertQuo(Fixed xk,Fixed yk,Fixed xl,Fixed yl)108 VertQuo(Fixed xk, Fixed yk, Fixed xl, Fixed yl)
109 {
110     /* FixOne means exactly vertical. 0 means not vertical */
111     /* intermediate values mean almost vertical */
112     Fixed xabs, yabs;
113     float rx, ry, q;
114     xabs = xk - xl;
115     if (xabs < 0)
116         xabs = -xabs;
117     if (xabs == 0)
118         return FixOne;
119     yabs = yk - yl;
120     if (yabs < 0)
121         yabs = -yabs;
122     if (yabs == 0)
123         return 0;
124     acfixtopflt(xabs, &rx);
125     acfixtopflt(yabs, &ry);
126     q = (float)(rx * rx) / (gTheta * ry); /* DEBUG 8 BIT. Used to by
127                                             2*(rx*rx)/(theta*ry). Don't need
128                                             thsi with the 8 bits of Fixed
129                                             fraction. */
130     return HVness(&q);
131 }
132 
133 Fixed
HorzQuo(Fixed xk,Fixed yk,Fixed xl,Fixed yl)134 HorzQuo(Fixed xk, Fixed yk, Fixed xl, Fixed yl)
135 {
136     Fixed xabs, yabs;
137     float rx, ry, q;
138     yabs = yk - yl;
139     if (yabs < 0)
140         yabs = -yabs;
141     if (yabs == 0)
142         return FixOne;
143     xabs = xk - xl;
144     if (xabs < 0)
145         xabs = -xabs;
146     if (xabs == 0)
147         return 0;
148     acfixtopflt(xabs, &rx);
149     acfixtopflt(yabs, &ry);
150     q = (float)(ry * ry) / (gTheta * rx); /* DEBUG 8 BIT. Used to by
151                                             2*(ry*ry)/(theta*ry). Don't need
152                                             thsi with the 8 bits of Fixed
153                                             fraction. */
154     return HVness(&q);
155 }
156 
157 bool
IsTiny(PathElt * e)158 IsTiny(PathElt* e)
159 {
160     Fixed x0 = 0, y0 = 0, x1 = 0, y1 = 0;
161     GetEndPoints(e, &x0, &y0, &x1, &y1);
162     return ((abs(x0 - x1) < FixTwo) && (abs(y0 - y1) < FixTwo)) ? true : false;
163 }
164 
165 bool
IsShort(PathElt * e)166 IsShort(PathElt* e)
167 {
168     Fixed x0 = 0, y0 = 0, x1 = 0, y1 = 0, dx = 0, dy = 0, mn = 0, mx = 0;
169     GetEndPoints(e, &x0, &y0, &x1, &y1);
170     dx = abs(x0 - x1);
171     dy = abs(y0 - y1);
172     if (dx > dy) {
173         mn = dy;
174         mx = dx;
175     } else {
176         mn = dx;
177         mx = dy;
178     }
179     return ((mx + (mn * 42) / 125) < FixInt(6))
180              ? true
181              : false; /* DEBUG 8 BIT. Increased threshold from 3 to 6, for
182                          change in coordinare system. */
183 }
184 
185 PathElt*
NxtForBend(PathElt * p,Fixed * px2,Fixed * py2,Fixed * px3,Fixed * py3)186 NxtForBend(PathElt* p, Fixed* px2, Fixed* py2, Fixed* px3, Fixed* py3)
187 {
188     PathElt *nxt, *nxtMT = NULL;
189     Fixed x = 0, y = 0;
190     nxt = p;
191     GetEndPoint(p, &x, &y);
192     while (true) {
193         if (nxt->type == CLOSEPATH) {
194             nxt = GetDest(nxt);
195             /* The following test was added to prevent an infinite loop. */
196             if (nxtMT != NULL && nxtMT == nxt) {
197                 ReportPossibleLoop(p);
198                 nxt = NULL;
199             } else {
200                 nxtMT = nxt;
201                 nxt = nxt->next;
202             }
203         } else
204             nxt = nxt->next;
205         if (nxt == NULL) { /* forget it */
206             *px2 = *py2 = *px3 = *py3 = -FixInt(9999);
207             return nxt;
208         }
209         if (!IsTiny(nxt))
210             break;
211     }
212     if (nxt->type == CURVETO) {
213         Fixed x2 = nxt->x1;
214         Fixed y2 = nxt->y1;
215         if (x2 == x && y2 == y) {
216             x2 = nxt->x2;
217             y2 = nxt->y2;
218         }
219         *px2 = x2;
220         *py2 = y2;
221     } else
222         GetEndPoint(nxt, px2, py2);
223     GetEndPoint(nxt, px3, py3);
224     return nxt;
225 }
226 
227 PathElt*
PrvForBend(PathElt * p,Fixed * px2,Fixed * py2)228 PrvForBend(PathElt* p, Fixed* px2, Fixed* py2)
229 {
230     PathElt *prv, *prvCP = NULL;
231     Fixed x2, y2;
232     prv = p;
233     while (true) {
234         prv = prv->prev;
235         if (prv == NULL)
236             goto Bogus;
237         if (prv->type == MOVETO) {
238             prv = GetClosedBy(prv);
239             /* The following test was added to prevent an infinite loop. */
240             if (prv == NULL || (prvCP != NULL && prvCP == prv))
241                 goto Bogus;
242             prvCP = prv;
243         }
244         if (!IsTiny(prv))
245             break;
246     }
247     if (prv->type == CURVETO) {
248         x2 = prv->x2;
249         y2 = prv->y2;
250         if (x2 == prv->x3 && y2 == prv->y3) {
251             x2 = prv->x1;
252             y2 = prv->y1;
253         }
254         *px2 = x2;
255         *py2 = y2;
256     } else {
257         p = prv->prev;
258         if (p == NULL)
259             goto Bogus;
260         GetEndPoint(p, px2, py2);
261     }
262     return prv;
263 Bogus:
264     *px2 = *py2 = -FixInt(9999);
265     return prv;
266 }
267 
268 static bool
CheckHeight(bool upperFlag,PathElt * p)269 CheckHeight(bool upperFlag, PathElt* p)
270 {
271     PathElt* ee;
272     Fixed y, yy;
273     ee = gPathStart;
274     y = -p->y;
275     while (ee != NULL) {
276         if (ee->type == MOVETO && ee != p) {
277             yy = -ee->y;
278             if ((upperFlag && yy > y) || (!upperFlag && yy < y))
279                 return false;
280         }
281         ee = ee->next;
282     }
283     return true;
284 }
285 
286 bool
IsLower(PathElt * p)287 IsLower(PathElt* p)
288 {
289     return CheckHeight(false, p);
290 }
291 
292 bool
IsUpper(PathElt * p)293 IsUpper(PathElt* p)
294 {
295     return CheckHeight(true, p);
296 }
297