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 */