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