Mercurial > MadButterfly
comparison src/shape_path.c @ 5:9c331ec9e210
SVG path is partially supported
author | Thinker K.F. Li <thinker@branda.to> |
---|---|
date | Sat, 26 Jul 2008 01:37:35 +0800 |
parents | |
children | 772511b8b9be |
comparison
equal
deleted
inserted
replaced
4:399517bf65dc | 5:9c331ec9e210 |
---|---|
1 #include <stdio.h> | |
2 #include <stdlib.h> | |
3 #include <ctype.h> | |
4 #include <string.h> | |
5 #include <cairo.h> | |
6 #include "mb_types.h" | |
7 | |
8 typedef struct _sh_path { | |
9 shape_t shape; | |
10 int cmd_len; | |
11 int arg_len; | |
12 char *user_data; | |
13 char *dev_data; /* device space data */ | |
14 } sh_path_t; | |
15 | |
16 #define ASSERT(x) | |
17 #define SKIP_SPACE(x) while(*(x) && isspace(*(x))) { (x)++; } | |
18 #define SKIP_NUM(x) \ | |
19 while(*(x) && \ | |
20 (isdigit(*(x)) || \ | |
21 *(x) == '-' || \ | |
22 *(x) == '+')) { \ | |
23 (x)++; \ | |
24 } | |
25 #define OK 0 | |
26 #define ERR -1 | |
27 | |
28 void sh_path_free(shape_t *shape) { | |
29 sh_path_t *path = (sh_path_t *)shape; | |
30 if(path->user_data) | |
31 free(path->user_data); | |
32 if(path->dev_data) | |
33 free(path->dev_data); | |
34 free(path); | |
35 } | |
36 | |
37 static int sh_path_cmd_arg_cnt(char *data, int *cmd_cntp, int *arg_cntp) { | |
38 char *p, *old; | |
39 int cmd_cnt, arg_cnt; | |
40 | |
41 cmd_cnt = arg_cnt = 0; | |
42 p = data; | |
43 while(*p) { | |
44 SKIP_SPACE(p); | |
45 | |
46 switch(*p++) { | |
47 case 'c': | |
48 case 'C': | |
49 while(*p) { | |
50 old = p; | |
51 SKIP_SPACE(p); | |
52 old = p; | |
53 SKIP_NUM(p); | |
54 if(p == old) | |
55 break; | |
56 arg_cnt++; | |
57 | |
58 SKIP_SPACE(p); | |
59 old = p; | |
60 SKIP_NUM(p); | |
61 if(p == old) | |
62 return ERR; | |
63 arg_cnt++; | |
64 | |
65 SKIP_SPACE(p); | |
66 old = p; | |
67 SKIP_NUM(p); | |
68 if(p == old) | |
69 return ERR; | |
70 arg_cnt++; | |
71 | |
72 SKIP_SPACE(p); | |
73 old = p; | |
74 SKIP_NUM(p); | |
75 if(p == old) | |
76 return ERR; | |
77 arg_cnt++; | |
78 SKIP_SPACE(p); | |
79 old = p; | |
80 SKIP_NUM(p); | |
81 if(p == old) | |
82 return ERR; | |
83 arg_cnt++; | |
84 | |
85 SKIP_SPACE(p); | |
86 old = p; | |
87 SKIP_NUM(p); | |
88 if(p == old) | |
89 return ERR; | |
90 arg_cnt++; | |
91 } | |
92 break; | |
93 case 's': | |
94 case 'S': | |
95 case 'q': | |
96 case 'Q': | |
97 while(*p) { | |
98 old = p; | |
99 SKIP_SPACE(p); | |
100 old = p; | |
101 SKIP_NUM(p); | |
102 if(p == old) | |
103 break; | |
104 arg_cnt++; | |
105 | |
106 SKIP_SPACE(p); | |
107 old = p; | |
108 SKIP_NUM(p); | |
109 if(p == old) | |
110 return ERR; | |
111 arg_cnt++; | |
112 | |
113 SKIP_SPACE(p); | |
114 old = p; | |
115 SKIP_NUM(p); | |
116 if(p == old) | |
117 return ERR; | |
118 arg_cnt++; | |
119 | |
120 SKIP_SPACE(p); | |
121 old = p; | |
122 SKIP_NUM(p); | |
123 if(p == old) | |
124 return ERR; | |
125 arg_cnt++; | |
126 } | |
127 break; | |
128 case 'm': | |
129 case 'M': | |
130 case 'l': | |
131 case 'L': | |
132 case 't': | |
133 case 'T': | |
134 while(*p) { | |
135 old = p; | |
136 SKIP_SPACE(p); | |
137 old = p; | |
138 SKIP_NUM(p); | |
139 if(p == old) | |
140 break; | |
141 arg_cnt++; | |
142 | |
143 SKIP_SPACE(p); | |
144 old = p; | |
145 SKIP_NUM(p); | |
146 if(p == old) | |
147 return ERR; | |
148 arg_cnt++; | |
149 } | |
150 break; | |
151 case 'h': | |
152 case 'H': | |
153 case 'v': | |
154 case 'V': | |
155 while(*p) { | |
156 SKIP_SPACE(p); | |
157 old = p; | |
158 SKIP_NUM(p); | |
159 if(p == old) | |
160 break; | |
161 arg_cnt += 2; | |
162 } | |
163 break; | |
164 case 'z': | |
165 case 'Z': | |
166 break; | |
167 default: | |
168 return ERR; | |
169 } | |
170 cmd_cnt++; | |
171 } | |
172 | |
173 *cmd_cntp = cmd_cnt; | |
174 *arg_cntp = arg_cnt; | |
175 return OK; | |
176 } | |
177 | |
178 static int sh_path_cmd_arg_fill(char *data, sh_path_t *path) { | |
179 char *p, *old; | |
180 char *cmds; | |
181 co_aix *args; | |
182 | |
183 cmds = path->user_data; | |
184 args = (co_aix *)(cmds + path->cmd_len); | |
185 p = data; | |
186 while(*p) { | |
187 SKIP_SPACE(p); | |
188 | |
189 *cmds++ = *p; | |
190 switch(*p++) { | |
191 case 'c': | |
192 case 'C': | |
193 while(*p) { | |
194 old = p; | |
195 SKIP_SPACE(p); | |
196 old = p; | |
197 SKIP_NUM(p); | |
198 if(p == old) | |
199 break; | |
200 *args++ = atof(old); | |
201 | |
202 SKIP_SPACE(p); | |
203 old = p; | |
204 SKIP_NUM(p); | |
205 if(p == old) | |
206 return ERR; | |
207 *args++ = atof(old); | |
208 | |
209 SKIP_SPACE(p); | |
210 old = p; | |
211 SKIP_NUM(p); | |
212 if(p == old) | |
213 return ERR; | |
214 *args++ = atof(old); | |
215 | |
216 SKIP_SPACE(p); | |
217 old = p; | |
218 SKIP_NUM(p); | |
219 if(p == old) | |
220 return ERR; | |
221 *args++ = atof(old); | |
222 SKIP_SPACE(p); | |
223 old = p; | |
224 SKIP_NUM(p); | |
225 if(p == old) | |
226 return ERR; | |
227 *args++ = atof(old); | |
228 | |
229 SKIP_SPACE(p); | |
230 old = p; | |
231 SKIP_NUM(p); | |
232 if(p == old) | |
233 return ERR; | |
234 *args++ = atof(old); | |
235 } | |
236 break; | |
237 case 's': | |
238 case 'S': | |
239 case 'q': | |
240 case 'Q': | |
241 while(*p) { | |
242 old = p; | |
243 SKIP_SPACE(p); | |
244 old = p; | |
245 SKIP_NUM(p); | |
246 if(p == old) | |
247 break; | |
248 *args++ = atof(old); | |
249 | |
250 SKIP_SPACE(p); | |
251 old = p; | |
252 SKIP_NUM(p); | |
253 if(p == old) | |
254 return ERR; | |
255 *args++ = atof(old); | |
256 | |
257 SKIP_SPACE(p); | |
258 old = p; | |
259 SKIP_NUM(p); | |
260 if(p == old) | |
261 return ERR; | |
262 *args++ = atof(old); | |
263 | |
264 SKIP_SPACE(p); | |
265 old = p; | |
266 SKIP_NUM(p); | |
267 if(p == old) | |
268 return ERR; | |
269 *args++ = atof(old); | |
270 } | |
271 break; | |
272 case 'm': | |
273 case 'M': | |
274 case 'l': | |
275 case 'L': | |
276 case 't': | |
277 case 'T': | |
278 while(*p) { | |
279 old = p; | |
280 SKIP_SPACE(p); | |
281 old = p; | |
282 SKIP_NUM(p); | |
283 if(p == old) | |
284 break; | |
285 *args++ = atof(old); | |
286 | |
287 SKIP_SPACE(p); | |
288 old = p; | |
289 SKIP_NUM(p); | |
290 if(p == old) | |
291 return ERR; | |
292 *args++ = atof(old); | |
293 } | |
294 break; | |
295 case 'h': | |
296 case 'H': | |
297 case 'v': | |
298 case 'V': | |
299 /* TODO: implement h, H, v, V comamnds for path. */ | |
300 return ERR; | |
301 case 'z': | |
302 case 'Z': | |
303 break; | |
304 default: | |
305 return ERR; | |
306 } | |
307 } | |
308 | |
309 return OK; | |
310 } | |
311 | |
312 shape_t *sh_path_new(char *data) { | |
313 sh_path_t *path; | |
314 int cmd_cnt, arg_cnt; | |
315 int r; | |
316 | |
317 r = sh_path_cmd_arg_cnt(data, &cmd_cnt, &arg_cnt); | |
318 if(r == ERR) | |
319 return NULL; | |
320 | |
321 cmd_cnt = (cmd_cnt + 3) & ~0x3; | |
322 | |
323 path = (sh_path_t *)malloc(sizeof(sh_path_t)); | |
324 path->shape.sh_type = SHT_PATH; | |
325 path->cmd_len = cmd_cnt; | |
326 path->arg_len = arg_cnt; | |
327 path->user_data = (char *)malloc(cmd_cnt + sizeof(co_aix) * arg_cnt); | |
328 if(path->user_data == NULL) { | |
329 free(path); | |
330 return NULL; | |
331 } | |
332 path->dev_data = (char *)malloc(cmd_cnt + sizeof(co_aix) * arg_cnt); | |
333 if(path->dev_data == NULL) { | |
334 free(path->dev_data); | |
335 free(path); | |
336 return NULL; | |
337 } | |
338 | |
339 memset(path->user_data, 0, cmd_cnt); | |
340 r = sh_path_cmd_arg_fill(data, path); | |
341 if(r == ERR) { | |
342 free(path->user_data); | |
343 free(path->dev_data); | |
344 free(path); | |
345 return NULL; | |
346 } | |
347 memcpy(path->dev_data, path->user_data, cmd_cnt); | |
348 | |
349 return (shape_t *)path; | |
350 } | |
351 | |
352 void sh_path_transform(shape_t *shape, coord_t *coord) { | |
353 sh_path_t *path; | |
354 co_aix *user_args, *dev_args; | |
355 int i; | |
356 | |
357 ASSERT(shape->type == SHT_PATH); | |
358 ASSERT((shape->arg_len & 0x1) == 0); | |
359 | |
360 path = (sh_path_t *)shape; | |
361 user_args = (co_aix *)(path->user_data + path->cmd_len); | |
362 dev_args = (co_aix *)(path->dev_data + path->cmd_len); | |
363 for(i = 0; i < path->arg_len; i += 2) { | |
364 dev_args[0] = *user_args++; | |
365 dev_args[1] = *user_args++; | |
366 coord_trans_pos(coord, dev_args, dev_args + 1); | |
367 dev_args += 2; | |
368 } | |
369 } | |
370 | |
371 void sh_path_draw(shape_t *shape, cairo_t *cr) { | |
372 sh_path_t *path; | |
373 int cmd_len; | |
374 char *cmds, cmd; | |
375 co_aix *args; | |
376 co_aix x, y, x1, y1, x2, y2; | |
377 int i; | |
378 | |
379 ASSERT(shape->type == SHT_PATH); | |
380 | |
381 path = (sh_path_t *)shape; | |
382 cmd_len = path->cmd_len; | |
383 cmds = path->dev_data; | |
384 args = (co_aix *)(cmds + cmd_len); | |
385 x = y = 0; | |
386 for(i = 0; i < cmd_len; i++) { | |
387 cmd = *cmds++; | |
388 switch(cmd) { | |
389 case 'm': | |
390 x = x + *args++; | |
391 y = y + *args++; | |
392 cairo_move_to(cr, x, y); | |
393 break; | |
394 case 'M': | |
395 x = *args++; | |
396 y = *args++; | |
397 cairo_move_to(cr, x, y); | |
398 break; | |
399 case 'l': | |
400 x = x + *args++; | |
401 y = y + *args++; | |
402 cairo_line_to(cr, x, y); | |
403 break; | |
404 case 'L': | |
405 x = *args++; | |
406 y = *args++; | |
407 cairo_line_to(cr, x, y); | |
408 break; | |
409 case 'c': | |
410 x1 = x + *args++; | |
411 y1 = y + *args++; | |
412 x2 = x1 + *args++; | |
413 y2 = y1 + *args++; | |
414 x = x2 + *args++; | |
415 y = y2 + *args++; | |
416 cairo_curve_to(cr, x1, y1, x2, y2, x, y); | |
417 break; | |
418 case 'C': | |
419 x1 = *args++; | |
420 y1 = *args++; | |
421 x2 = *args++; | |
422 y2 = *args++; | |
423 x = *args++; | |
424 y = *args++; | |
425 cairo_curve_to(cr, x1, y1, x2, y2, x, y); | |
426 break; | |
427 case 's': | |
428 x1 = x + x - x2; | |
429 y1 = y + y - y2; | |
430 x2 = x1 + *args++; | |
431 y2 = y1 + *args++; | |
432 x = x2 + *args++; | |
433 y = y2 + *args++; | |
434 cairo_curve_to(cr, x1, y1, x2, y2, x, y); | |
435 break; | |
436 case 'S': | |
437 x1 = x + x - x2; | |
438 y1 = y + y - y2; | |
439 x2 = *args++; | |
440 y2 = *args++; | |
441 x = *args++; | |
442 y = *args++; | |
443 cairo_curve_to(cr, x1, y1, x2, y2, x, y); | |
444 break; | |
445 case 'q': | |
446 x1 = x + *args++; | |
447 y1 = y + *args++; | |
448 x2 = x1; | |
449 y2 = y1; | |
450 x = x2 + *args++; | |
451 y = y2 + *args++; | |
452 cairo_curve_to(cr, x1, y1, x2, y2, x, y); | |
453 break; | |
454 case 'Q': | |
455 x1 = *args++; | |
456 y1 = *args++; | |
457 x2 = x1; | |
458 y2 = y1; | |
459 x = *args++; | |
460 y = *args++; | |
461 cairo_curve_to(cr, x1, y1, x2, y2, x, y); | |
462 break; | |
463 case 't': | |
464 x1 = x + x - x2; | |
465 y1 = y + y - y2; | |
466 x2 = x1; | |
467 y2 = y1; | |
468 x = x2 + *args++; | |
469 y = y2 + *args++; | |
470 cairo_curve_to(cr, x1, y1, x2, y2, x, y); | |
471 break; | |
472 case 'T': | |
473 x1 = x + x - x2; | |
474 y1 = y + y - y2; | |
475 x2 = x1; | |
476 y2 = y1; | |
477 x = *args++; | |
478 y = *args++; | |
479 cairo_curve_to(cr, x1, y1, x2, y2, x, y); | |
480 break; | |
481 case 'Z': | |
482 case 'z': | |
483 cairo_close_path(cr); | |
484 break; | |
485 case '\x0': | |
486 break; | |
487 } | |
488 } | |
489 } | |
490 | |
491 #ifdef UNITTEST | |
492 | |
493 #include <CUnit/Basic.h> | |
494 | |
495 void test_sh_path_new(void) { | |
496 sh_path_t *path; | |
497 co_aix *args; | |
498 | |
499 path = (sh_path_t *)sh_path_new("M 33 25l33 55C 33 87 44 22 55 99L33 77z"); | |
500 CU_ASSERT(path != NULL); | |
501 CU_ASSERT(path->cmd_len == 8); | |
502 CU_ASSERT(path->arg_len == 12); | |
503 CU_ASSERT(strcmp(path->user_data, "MlCLz") == 0); | |
504 CU_ASSERT(strcmp(path->dev_data, "MlCLz") == 0); | |
505 | |
506 args = (co_aix *)(path->user_data + path->cmd_len); | |
507 CU_ASSERT(args[0] == 33); | |
508 CU_ASSERT(args[1] == 25); | |
509 CU_ASSERT(args[2] == 33); | |
510 CU_ASSERT(args[3] == 55); | |
511 CU_ASSERT(args[4] == 33); | |
512 CU_ASSERT(args[5] == 87); | |
513 CU_ASSERT(args[6] == 44); | |
514 CU_ASSERT(args[7] == 22); | |
515 CU_ASSERT(args[8] == 55); | |
516 CU_ASSERT(args[9] == 99); | |
517 CU_ASSERT(args[10] == 33); | |
518 CU_ASSERT(args[11] == 77); | |
519 sh_path_free((shape_t *)path); | |
520 } | |
521 | |
522 void test_path_transform(void) { | |
523 sh_path_t *path; | |
524 co_aix *args; | |
525 coord_t coord; | |
526 | |
527 path = (sh_path_t *)sh_path_new("M 33 25l33 55C 33 87 44 22 55 99L33 77z"); | |
528 CU_ASSERT(path != NULL); | |
529 CU_ASSERT(path->cmd_len == 8); | |
530 CU_ASSERT(path->arg_len == 12); | |
531 CU_ASSERT(strcmp(path->user_data, "MlCLz") == 0); | |
532 CU_ASSERT(strcmp(path->dev_data, "MlCLz") == 0); | |
533 | |
534 coord.aggr_matrix[0] = 1; | |
535 coord.aggr_matrix[1] = 0; | |
536 coord.aggr_matrix[2] = 1; | |
537 coord.aggr_matrix[3] = 0; | |
538 coord.aggr_matrix[4] = 2; | |
539 coord.aggr_matrix[5] = 0; | |
540 sh_path_transform((shape_t *)path, &coord); | |
541 | |
542 args = (co_aix *)(path->dev_data + path->cmd_len); | |
543 CU_ASSERT(args[0] == 34); | |
544 CU_ASSERT(args[1] == 50); | |
545 CU_ASSERT(args[2] == 34); | |
546 CU_ASSERT(args[3] == 110); | |
547 CU_ASSERT(args[4] == 34); | |
548 CU_ASSERT(args[5] == 174); | |
549 CU_ASSERT(args[6] == 45); | |
550 CU_ASSERT(args[7] == 44); | |
551 CU_ASSERT(args[8] == 56); | |
552 CU_ASSERT(args[9] == 198); | |
553 CU_ASSERT(args[10] == 34); | |
554 CU_ASSERT(args[11] == 154); | |
555 sh_path_free((shape_t *)path); | |
556 } | |
557 | |
558 CU_pSuite get_shape_path_suite(void) { | |
559 CU_pSuite suite; | |
560 | |
561 suite = CU_add_suite("Suite_shape_path", NULL, NULL); | |
562 CU_ADD_TEST(suite, test_sh_path_new); | |
563 CU_ADD_TEST(suite, test_path_transform); | |
564 | |
565 return suite; | |
566 } | |
567 | |
568 #endif /* UNITTEST */ |