Mercurial > MadButterfly
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shape_path.c Sat Jul 26 01:37:35 2008 +0800 @@ -0,0 +1,568 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <cairo.h> +#include "mb_types.h" + +typedef struct _sh_path { + shape_t shape; + int cmd_len; + int arg_len; + char *user_data; + char *dev_data; /* device space data */ +} sh_path_t; + +#define ASSERT(x) +#define SKIP_SPACE(x) while(*(x) && isspace(*(x))) { (x)++; } +#define SKIP_NUM(x) \ + while(*(x) && \ + (isdigit(*(x)) || \ + *(x) == '-' || \ + *(x) == '+')) { \ + (x)++; \ + } +#define OK 0 +#define ERR -1 + +void sh_path_free(shape_t *shape) { + sh_path_t *path = (sh_path_t *)shape; + if(path->user_data) + free(path->user_data); + if(path->dev_data) + free(path->dev_data); + free(path); +} + +static int sh_path_cmd_arg_cnt(char *data, int *cmd_cntp, int *arg_cntp) { + char *p, *old; + int cmd_cnt, arg_cnt; + + cmd_cnt = arg_cnt = 0; + p = data; + while(*p) { + SKIP_SPACE(p); + + switch(*p++) { + case 'c': + case 'C': + while(*p) { + old = p; + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + break; + arg_cnt++; + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + } + break; + case 's': + case 'S': + case 'q': + case 'Q': + while(*p) { + old = p; + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + break; + arg_cnt++; + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + } + break; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + while(*p) { + old = p; + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + break; + arg_cnt++; + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + arg_cnt++; + } + break; + case 'h': + case 'H': + case 'v': + case 'V': + while(*p) { + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + break; + arg_cnt += 2; + } + break; + case 'z': + case 'Z': + break; + default: + return ERR; + } + cmd_cnt++; + } + + *cmd_cntp = cmd_cnt; + *arg_cntp = arg_cnt; + return OK; +} + +static int sh_path_cmd_arg_fill(char *data, sh_path_t *path) { + char *p, *old; + char *cmds; + co_aix *args; + + cmds = path->user_data; + args = (co_aix *)(cmds + path->cmd_len); + p = data; + while(*p) { + SKIP_SPACE(p); + + *cmds++ = *p; + switch(*p++) { + case 'c': + case 'C': + while(*p) { + old = p; + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + break; + *args++ = atof(old); + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + } + break; + case 's': + case 'S': + case 'q': + case 'Q': + while(*p) { + old = p; + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + break; + *args++ = atof(old); + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + } + break; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + while(*p) { + old = p; + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + break; + *args++ = atof(old); + + SKIP_SPACE(p); + old = p; + SKIP_NUM(p); + if(p == old) + return ERR; + *args++ = atof(old); + } + break; + case 'h': + case 'H': + case 'v': + case 'V': + /* TODO: implement h, H, v, V comamnds for path. */ + return ERR; + case 'z': + case 'Z': + break; + default: + return ERR; + } + } + + return OK; +} + +shape_t *sh_path_new(char *data) { + sh_path_t *path; + int cmd_cnt, arg_cnt; + int r; + + r = sh_path_cmd_arg_cnt(data, &cmd_cnt, &arg_cnt); + if(r == ERR) + return NULL; + + cmd_cnt = (cmd_cnt + 3) & ~0x3; + + path = (sh_path_t *)malloc(sizeof(sh_path_t)); + path->shape.sh_type = SHT_PATH; + path->cmd_len = cmd_cnt; + path->arg_len = arg_cnt; + path->user_data = (char *)malloc(cmd_cnt + sizeof(co_aix) * arg_cnt); + if(path->user_data == NULL) { + free(path); + return NULL; + } + path->dev_data = (char *)malloc(cmd_cnt + sizeof(co_aix) * arg_cnt); + if(path->dev_data == NULL) { + free(path->dev_data); + free(path); + return NULL; + } + + memset(path->user_data, 0, cmd_cnt); + r = sh_path_cmd_arg_fill(data, path); + if(r == ERR) { + free(path->user_data); + free(path->dev_data); + free(path); + return NULL; + } + memcpy(path->dev_data, path->user_data, cmd_cnt); + + return (shape_t *)path; +} + +void sh_path_transform(shape_t *shape, coord_t *coord) { + sh_path_t *path; + co_aix *user_args, *dev_args; + int i; + + ASSERT(shape->type == SHT_PATH); + ASSERT((shape->arg_len & 0x1) == 0); + + path = (sh_path_t *)shape; + user_args = (co_aix *)(path->user_data + path->cmd_len); + dev_args = (co_aix *)(path->dev_data + path->cmd_len); + for(i = 0; i < path->arg_len; i += 2) { + dev_args[0] = *user_args++; + dev_args[1] = *user_args++; + coord_trans_pos(coord, dev_args, dev_args + 1); + dev_args += 2; + } +} + +void sh_path_draw(shape_t *shape, cairo_t *cr) { + sh_path_t *path; + int cmd_len; + char *cmds, cmd; + co_aix *args; + co_aix x, y, x1, y1, x2, y2; + int i; + + ASSERT(shape->type == SHT_PATH); + + path = (sh_path_t *)shape; + cmd_len = path->cmd_len; + cmds = path->dev_data; + args = (co_aix *)(cmds + cmd_len); + x = y = 0; + for(i = 0; i < cmd_len; i++) { + cmd = *cmds++; + switch(cmd) { + case 'm': + x = x + *args++; + y = y + *args++; + cairo_move_to(cr, x, y); + break; + case 'M': + x = *args++; + y = *args++; + cairo_move_to(cr, x, y); + break; + case 'l': + x = x + *args++; + y = y + *args++; + cairo_line_to(cr, x, y); + break; + case 'L': + x = *args++; + y = *args++; + cairo_line_to(cr, x, y); + break; + case 'c': + x1 = x + *args++; + y1 = y + *args++; + x2 = x1 + *args++; + y2 = y1 + *args++; + x = x2 + *args++; + y = y2 + *args++; + cairo_curve_to(cr, x1, y1, x2, y2, x, y); + break; + case 'C': + x1 = *args++; + y1 = *args++; + x2 = *args++; + y2 = *args++; + x = *args++; + y = *args++; + cairo_curve_to(cr, x1, y1, x2, y2, x, y); + break; + case 's': + x1 = x + x - x2; + y1 = y + y - y2; + x2 = x1 + *args++; + y2 = y1 + *args++; + x = x2 + *args++; + y = y2 + *args++; + cairo_curve_to(cr, x1, y1, x2, y2, x, y); + break; + case 'S': + x1 = x + x - x2; + y1 = y + y - y2; + x2 = *args++; + y2 = *args++; + x = *args++; + y = *args++; + cairo_curve_to(cr, x1, y1, x2, y2, x, y); + break; + case 'q': + x1 = x + *args++; + y1 = y + *args++; + x2 = x1; + y2 = y1; + x = x2 + *args++; + y = y2 + *args++; + cairo_curve_to(cr, x1, y1, x2, y2, x, y); + break; + case 'Q': + x1 = *args++; + y1 = *args++; + x2 = x1; + y2 = y1; + x = *args++; + y = *args++; + cairo_curve_to(cr, x1, y1, x2, y2, x, y); + break; + case 't': + x1 = x + x - x2; + y1 = y + y - y2; + x2 = x1; + y2 = y1; + x = x2 + *args++; + y = y2 + *args++; + cairo_curve_to(cr, x1, y1, x2, y2, x, y); + break; + case 'T': + x1 = x + x - x2; + y1 = y + y - y2; + x2 = x1; + y2 = y1; + x = *args++; + y = *args++; + cairo_curve_to(cr, x1, y1, x2, y2, x, y); + break; + case 'Z': + case 'z': + cairo_close_path(cr); + break; + case '\x0': + break; + } + } +} + +#ifdef UNITTEST + +#include <CUnit/Basic.h> + +void test_sh_path_new(void) { + sh_path_t *path; + co_aix *args; + + path = (sh_path_t *)sh_path_new("M 33 25l33 55C 33 87 44 22 55 99L33 77z"); + CU_ASSERT(path != NULL); + CU_ASSERT(path->cmd_len == 8); + CU_ASSERT(path->arg_len == 12); + CU_ASSERT(strcmp(path->user_data, "MlCLz") == 0); + CU_ASSERT(strcmp(path->dev_data, "MlCLz") == 0); + + args = (co_aix *)(path->user_data + path->cmd_len); + CU_ASSERT(args[0] == 33); + CU_ASSERT(args[1] == 25); + CU_ASSERT(args[2] == 33); + CU_ASSERT(args[3] == 55); + CU_ASSERT(args[4] == 33); + CU_ASSERT(args[5] == 87); + CU_ASSERT(args[6] == 44); + CU_ASSERT(args[7] == 22); + CU_ASSERT(args[8] == 55); + CU_ASSERT(args[9] == 99); + CU_ASSERT(args[10] == 33); + CU_ASSERT(args[11] == 77); + sh_path_free((shape_t *)path); +} + +void test_path_transform(void) { + sh_path_t *path; + co_aix *args; + coord_t coord; + + path = (sh_path_t *)sh_path_new("M 33 25l33 55C 33 87 44 22 55 99L33 77z"); + CU_ASSERT(path != NULL); + CU_ASSERT(path->cmd_len == 8); + CU_ASSERT(path->arg_len == 12); + CU_ASSERT(strcmp(path->user_data, "MlCLz") == 0); + CU_ASSERT(strcmp(path->dev_data, "MlCLz") == 0); + + coord.aggr_matrix[0] = 1; + coord.aggr_matrix[1] = 0; + coord.aggr_matrix[2] = 1; + coord.aggr_matrix[3] = 0; + coord.aggr_matrix[4] = 2; + coord.aggr_matrix[5] = 0; + sh_path_transform((shape_t *)path, &coord); + + args = (co_aix *)(path->dev_data + path->cmd_len); + CU_ASSERT(args[0] == 34); + CU_ASSERT(args[1] == 50); + CU_ASSERT(args[2] == 34); + CU_ASSERT(args[3] == 110); + CU_ASSERT(args[4] == 34); + CU_ASSERT(args[5] == 174); + CU_ASSERT(args[6] == 45); + CU_ASSERT(args[7] == 44); + CU_ASSERT(args[8] == 56); + CU_ASSERT(args[9] == 198); + CU_ASSERT(args[10] == 34); + CU_ASSERT(args[11] == 154); + sh_path_free((shape_t *)path); +} + +CU_pSuite get_shape_path_suite(void) { + CU_pSuite suite; + + suite = CU_add_suite("Suite_shape_path", NULL, NULL); + CU_ADD_TEST(suite, test_sh_path_new); + CU_ADD_TEST(suite, test_path_transform); + + return suite; +} + +#endif /* UNITTEST */