1 | |
2 | #define _CRT_SECURE_NO_WARNINGS |
3 | #include "shape-description.h" |
4 | |
5 | namespace msdfgen { |
6 | |
7 | int readCharF(FILE *input) { |
8 | int c = '\0'; |
9 | do { |
10 | c = fgetc(input); |
11 | } while (c == ' ' || c == '\t' || c == '\r' || c == '\n'); |
12 | return c; |
13 | } |
14 | |
15 | int readCharS(const char **input) { |
16 | int c = '\0'; |
17 | do { |
18 | c = *(*input)++; |
19 | } while (c == ' ' || c == '\t' || c == '\r' || c == '\n'); |
20 | if (!c) { |
21 | --c; |
22 | return EOF; |
23 | } |
24 | return c; |
25 | } |
26 | |
27 | int readCoordF(FILE *input, Point2 &coord) { |
28 | return fscanf(input, "%lf,%lf" , &coord.x, &coord.y); |
29 | } |
30 | |
31 | int readCoordS(const char **input, Point2 &coord) { |
32 | int read = 0; |
33 | int result = sscanf(*input, "%lf,%lf%n" , &coord.x, &coord.y, &read); |
34 | *input += read; |
35 | return result; |
36 | } |
37 | |
38 | static bool writeCoord(FILE *output, Point2 coord) { |
39 | fprintf(output, "%.12g, %.12g" , coord.x, coord.y); |
40 | return true; |
41 | } |
42 | |
43 | template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)> |
44 | static int readControlPoints(T *input, Point2 *output) { |
45 | int result = readCoord(input, output[0]); |
46 | if (result == 2) { |
47 | switch (readChar(input)) { |
48 | case ')': |
49 | return 1; |
50 | case ';': |
51 | break; |
52 | default: |
53 | return -1; |
54 | } |
55 | result = readCoord(input, output[1]); |
56 | if (result == 2 && readChar(input) == ')') |
57 | return 2; |
58 | } else if (result != 1 && readChar(input) == ')') |
59 | return 0; |
60 | return -1; |
61 | } |
62 | |
63 | template <typename T, int (*readChar)(T *), int (*readCoord)(T *, Point2 &)> |
64 | static bool readContour(T *input, Contour &output, const Point2 *first, int terminator, bool &) { |
65 | Point2 p[4], start; |
66 | if (first) |
67 | p[0] = *first; |
68 | else { |
69 | int result = readCoord(input, p[0]); |
70 | if (result != 2) |
71 | return result != 1 && readChar(input) == terminator; |
72 | } |
73 | start = p[0]; |
74 | int c = '\0'; |
75 | while ((c = readChar(input)) != terminator) { |
76 | if (c != ';') |
77 | return false; |
78 | EdgeColor color = WHITE; |
79 | int result = readCoord(input, p[1]); |
80 | if (result == 2) { |
81 | output.addEdge(EdgeHolder(p[0], p[1], color)); |
82 | p[0] = p[1]; |
83 | continue; |
84 | } else if (result == 1) |
85 | return false; |
86 | else { |
87 | int controlPoints = 0; |
88 | switch ((c = readChar(input))) { |
89 | case '#': |
90 | output.addEdge(EdgeHolder(p[0], start, color)); |
91 | p[0] = start; |
92 | continue; |
93 | case ';': |
94 | goto FINISH_EDGE; |
95 | case '(': |
96 | goto READ_CONTROL_POINTS; |
97 | case 'C': case 'c': |
98 | color = CYAN; |
99 | colorsSpecified = true; |
100 | break; |
101 | case 'M': case 'm': |
102 | color = MAGENTA; |
103 | colorsSpecified = true; |
104 | break; |
105 | case 'Y': case 'y': |
106 | color = YELLOW; |
107 | colorsSpecified = true; |
108 | break; |
109 | case 'W': case 'w': |
110 | color = WHITE; |
111 | colorsSpecified = true; |
112 | break; |
113 | default: |
114 | return c == terminator; |
115 | } |
116 | switch (readChar(input)) { |
117 | case ';': |
118 | goto FINISH_EDGE; |
119 | case '(': |
120 | READ_CONTROL_POINTS: |
121 | if ((controlPoints = readControlPoints<T, readChar, readCoord>(input, p+1)) < 0) |
122 | return false; |
123 | break; |
124 | default: |
125 | return false; |
126 | } |
127 | if (readChar(input) != ';') |
128 | return false; |
129 | FINISH_EDGE: |
130 | result = readCoord(input, p[1+controlPoints]); |
131 | if (result != 2) { |
132 | if (result == 1) |
133 | return false; |
134 | else { |
135 | if (readChar(input) == '#') |
136 | p[1+controlPoints] = start; |
137 | else |
138 | return false; |
139 | } |
140 | } |
141 | switch (controlPoints) { |
142 | case 0: |
143 | output.addEdge(EdgeHolder(p[0], p[1], color)); |
144 | p[0] = p[1]; |
145 | continue; |
146 | case 1: |
147 | output.addEdge(EdgeHolder(p[0], p[1], p[2], color)); |
148 | p[0] = p[2]; |
149 | continue; |
150 | case 2: |
151 | output.addEdge(EdgeHolder(p[0], p[1], p[2], p[3], color)); |
152 | p[0] = p[3]; |
153 | continue; |
154 | } |
155 | } |
156 | } |
157 | return true; |
158 | } |
159 | |
160 | bool readShapeDescription(FILE *input, Shape &output, bool *) { |
161 | bool = false; |
162 | output.contours.clear(); |
163 | output.inverseYAxis = false; |
164 | Point2 p; |
165 | int result = readCoordF(input, p); |
166 | if (result == 2) { |
167 | return readContour<FILE, readCharF, readCoordF>(input, output.addContour(), &p, EOF, locColorsSpec); |
168 | } else if (result == 1) |
169 | return false; |
170 | else { |
171 | int c = readCharF(input); |
172 | if (c == '@') { |
173 | char after = '\0'; |
174 | if (fscanf(input, "invert-y%c" , &after) != 1) |
175 | return feof(input) != 0; |
176 | output.inverseYAxis = true; |
177 | c = after; |
178 | if (c == ' ' || c == '\t' || c == '\r' || c == '\n') |
179 | c = readCharF(input); |
180 | } |
181 | for (; c == '{'; c = readCharF(input)) |
182 | if (!readContour<FILE, readCharF, readCoordF>(input, output.addContour(), NULL, '}', locColorsSpec)) |
183 | return false; |
184 | if (colorsSpecified) |
185 | *colorsSpecified = locColorsSpec; |
186 | return c == EOF && feof(input); |
187 | } |
188 | } |
189 | |
190 | bool readShapeDescription(const char *input, Shape &output, bool *) { |
191 | bool = false; |
192 | output.contours.clear(); |
193 | output.inverseYAxis = false; |
194 | Point2 p; |
195 | int result = readCoordS(&input, p); |
196 | if (result == 2) { |
197 | return readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), &p, EOF, locColorsSpec); |
198 | } else if (result == 1) |
199 | return false; |
200 | else { |
201 | int c = readCharS(&input); |
202 | if (c == '@') { |
203 | for (int i = 0; i < (int) sizeof("invert-y" )-1; ++i) |
204 | if (input[i] != "invert-y" [i]) |
205 | return false; |
206 | output.inverseYAxis = true; |
207 | input += sizeof("invert-y" )-1; |
208 | c = readCharS(&input); |
209 | } |
210 | for (; c == '{'; c = readCharS(&input)) |
211 | if (!readContour<const char *, readCharS, readCoordS>(&input, output.addContour(), NULL, '}', locColorsSpec)) |
212 | return false; |
213 | if (colorsSpecified) |
214 | *colorsSpecified = locColorsSpec; |
215 | return c == EOF; |
216 | } |
217 | } |
218 | |
219 | static bool isColored(const Shape &shape) { |
220 | for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) |
221 | for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) |
222 | if ((*edge)->color != WHITE) |
223 | return true; |
224 | return false; |
225 | } |
226 | |
227 | bool writeShapeDescription(FILE *output, const Shape &shape) { |
228 | if (!shape.validate()) |
229 | return false; |
230 | bool writeColors = isColored(shape); |
231 | if (shape.inverseYAxis) |
232 | fprintf(output, "@invert-y\n" ); |
233 | for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { |
234 | fprintf(output, "{\n" ); |
235 | if (!contour->edges.empty()) { |
236 | for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { |
237 | char colorCode = '\0'; |
238 | if (writeColors) { |
239 | switch ((*edge)->color) { |
240 | case YELLOW: colorCode = 'y'; break; |
241 | case MAGENTA: colorCode = 'm'; break; |
242 | case CYAN: colorCode = 'c'; break; |
243 | case WHITE: colorCode = 'w'; break; |
244 | default:; |
245 | } |
246 | } |
247 | if (const LinearSegment *e = dynamic_cast<const LinearSegment *>(&**edge)) { |
248 | fprintf(output, "\t" ); |
249 | writeCoord(output, e->p[0]); |
250 | fprintf(output, ";\n" ); |
251 | if (colorCode) |
252 | fprintf(output, "\t\t%c;\n" , colorCode); |
253 | } |
254 | if (const QuadraticSegment *e = dynamic_cast<const QuadraticSegment *>(&**edge)) { |
255 | fprintf(output, "\t" ); |
256 | writeCoord(output, e->p[0]); |
257 | fprintf(output, ";\n\t\t" ); |
258 | if (colorCode) |
259 | fprintf(output, "%c" , colorCode); |
260 | fprintf(output, "(" ); |
261 | writeCoord(output, e->p[1]); |
262 | fprintf(output, ");\n" ); |
263 | } |
264 | if (const CubicSegment *e = dynamic_cast<const CubicSegment *>(&**edge)) { |
265 | fprintf(output, "\t" ); |
266 | writeCoord(output, e->p[0]); |
267 | fprintf(output, ";\n\t\t" ); |
268 | if (colorCode) |
269 | fprintf(output, "%c" , colorCode); |
270 | fprintf(output, "(" ); |
271 | writeCoord(output, e->p[1]); |
272 | fprintf(output, "; " ); |
273 | writeCoord(output, e->p[2]); |
274 | fprintf(output, ");\n" ); |
275 | } |
276 | } |
277 | fprintf(output, "\t#\n" ); |
278 | } |
279 | fprintf(output, "}\n" ); |
280 | } |
281 | return true; |
282 | } |
283 | |
284 | } |
285 | |