Mercurial > MadButterfly
changeset 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 | 399517bf65dc |
children | 772511b8b9be |
files | src/Makefile src/X_main.c src/coord.c src/geo.c src/mb_types.h src/shape_path.c src/shapes.h src/testcase.c |
diffstat | 8 files changed, 796 insertions(+), 21 deletions(-) [+] |
line wrap: on
line diff
--- a/src/Makefile Fri Jul 25 11:42:37 2008 +0800 +++ b/src/Makefile Sat Jul 26 01:37:35 2008 +0800 @@ -1,6 +1,8 @@ -SRCS = coord.c +SRCS = coord.c geo.c shape_path.c +OBJS = ${SRCS:C/(.*)\.c/\1.o/g} TESTCASE_OBJS = ${SRCS:C/(.*)\.c/testcase-\1.o/g} -CFLAGS = -I/usr/local/include +CFLAGS = -I/usr/local/include `pkg-config --cflags cairo` +LDFLAGS = `pkg-config --libs cairo` BINS = testcase X_main all: $(BINS) @@ -16,14 +18,14 @@ testcase.o: testcase.c $(CC) $(CFLAGS) -c $(.ALLSRC) -X_main: X_main.o +X_main: X_main.o $(OBJS) $(CC) $(CFALGS) `pkg-config --libs cairo` -o $@ $(.ALLSRC) X_main.o: X_main.c $(CC) $(CFLAGS) `pkg-config --cflags cairo` -c $(.ALLSRC) clean: - for i in *.o *~ *.core $(BINS); do \ + for i in *.o *~ *.core $(SHAPE_OBJS) $(BINS); do \ echo "delete $$i"; \ rm -f $$i; \ done
--- a/src/X_main.c Fri Jul 25 11:42:37 2008 +0800 +++ b/src/X_main.c Sat Jul 26 01:37:35 2008 +0800 @@ -3,11 +3,29 @@ #include <X11/Xlib.h> #include <cairo.h> +#include <string.h> +#include "shapes.h" + +void draw_path(cairo_t *cr, int w, int h) { + shape_t *path; + coord_t coord; + + path = sh_path_new("M80 80 c 20 5 -30 20 10 30 l -30 0 z"); + memset(coord.aggr_matrix, 0, sizeof(co_aix) * 6); + coord.aggr_matrix[0] = 1; + coord.aggr_matrix[1] = 0.5; + coord.aggr_matrix[4] = 1; + coord.aggr_matrix[5] = 0.5; + sh_path_transform(path, &coord); + sh_path_draw(path, cr); +} + void drawing(cairo_surface_t *surface, int w, int h) { cairo_t *cr; cr = cairo_create(surface); cairo_set_source_rgb(cr, 0xff, 0xff, 0x80); + draw_path(cr, w, h); cairo_move_to(cr, 10, h / 2); cairo_set_font_size(cr, 48.0); cairo_text_path(cr, "hello \xe4\xb8\xad\xe6\x96\x87");
--- a/src/coord.c Fri Jul 25 11:42:37 2008 +0800 +++ b/src/coord.c Sat Jul 26 01:37:35 2008 +0800 @@ -4,23 +4,7 @@ */ #include <stdio.h> #include <string.h> - -typedef float co_aix; -/*! \brief A coordination system. - * - * It have a transform function defined by matrix to transform - * coordination from source space to target space. - * Source space is where the contained is drawed, and target space - * is where the coordination of parent container of the element - * represented by this coord object. - */ -typedef struct coord { - int seq; - co_aix matrix[6]; - co_aix aggr_matrix[6]; - struct coord *parent; - struct coord *children, *sibling; -} coord_t; +#include "mb_types.h" /* To keep possibility of changing type of aix */ #define MUL(a, b) ((a) * (b))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/geo.c Sat Jul 26 01:37:35 2008 +0800 @@ -0,0 +1,154 @@ +/*! \brief Determine who should be re-drawed. + * \file + * When part of graphic are chagned, not mater size, shape, or position, + * the components effected or overlaid should be re-drawed. This module + * figures out components that should be re-drawed. + */ +#include <stdio.h> +#include "mb_types.h" + +struct _geo { + co_aix x, y; + co_aix w, h; + co_aix rel_x, rel_y; + co_aix orig_x, orig_y; + int n_subs; + struct _geo *subs; +}; + +static int is_pos_in(co_aix x, co_aix y, + co_aix rectx, co_aix recty, + co_aix rectw, co_aix recth) { + co_aix rel_x, rel_y; + + rel_x = x - rectx; + if(rel_x < 0 || rel_x >= rectw) + return 0; + rel_y = y - recty; + if(rel_y < 0 || rel_y >= recth) + return 0; + return 1; +} + +static int is_scale_overlay(co_aix x1, co_aix w1, co_aix x2, co_aix w2) { + if(x1 > x2) { + if((x1 - x2) >= w2) + return 0; + } else { + if((x2 - x1) >= w1) + return 0; + } + return 1; +} + +static int is_overlay(geo_t *r1, geo_t *r2) { + co_aix rel_x, rel_y; + + if(!is_scale_overlay(r1->x, r1->w, r2->x, r2->w)) + return 0; + if(!is_scale_overlay(r1->y, r1->h, r2->y, r2->h)) + return 0; + + return 1; +} + +void geo_init(geo_t *g, int n_pos, co_aix pos[][2]) { + co_aix min_x, max_x; + co_aix min_y, max_y; + co_aix x, y; + int i; + + min_x = max_x = pos[0][0]; + min_y = max_y = pos[0][1]; + for(i = 1; i < n_pos; i++) { + x = pos[i][0]; + if(x < min_x) + min_x = x; + else if(x > max_x) + max_x = x; + y = pos[i][1]; + if(y < min_y) + min_y = y; + else if(y > max_y) + max_y = y; + } + g->x = min_x; + g->w = max_x - min_x; + g->y = min_y; + g->h = max_y - min_y; +} + +void geo_mark_overlay(geo_t *g, int n_others, geo_t **others, + int *n_overlays, geo_t **overlays) { + int i, ov_idx; + + ov_idx = 0; + for(i = 0; i < n_others; i++) { + if(is_overlay(g, others[i])) + overlays[ov_idx++] = others[i]; + } + *n_overlays = ov_idx; +} + + +#ifdef UNITTEST + +#include <CUnit/Basic.h> + +void test_geo_init(void) { + co_aix data[][2] = { + {33, 25}, {49, 12}, + {14, 28}, {39, 56}}; + geo_t g; + + geo_init(&g, 4, data); + CU_ASSERT(g.x == 14); + CU_ASSERT(g.w == 35); + CU_ASSERT(g.y == 12); + CU_ASSERT(g.h == 44); +} + +void test_geo_mark_overlay(void) { + geo_t _geos[3], *geos[3], *overlays[3]; + geo_t g; + int i, n_ov; + + for(i = 0; i < 3; i++) { + _geos[i].x = i * 50; + _geos[i].y = i * 50; + _geos[i].w = 55; + _geos[i].h = 66; + geos[i] = _geos + i; + } + g.x = 88; + g.y = 79; + g.w = 70; + g.h = 70; + + /* overlay with geos[1] and geos[2] */ + geo_mark_overlay(&g, 3, geos, &n_ov, overlays); + CU_ASSERT(n_ov == 2); + CU_ASSERT(overlays[0] == geos[1]); + CU_ASSERT(overlays[1] == geos[2]); + + /* right side of geos[1], and up side of geos[2] */ + g.x = 105; + g.y = 50; + g.w = 50; + g.h = 51; + geo_mark_overlay(&g, 3, geos, &n_ov, overlays); + CU_ASSERT(n_ov == 1); + CU_ASSERT(overlays[0] == geos[2]); +} + +CU_pSuite get_geo_suite(void) { + CU_pSuite suite; + + suite = CU_add_suite("Suite_geo", NULL, NULL); + CU_ADD_TEST(suite, test_geo_init); + CU_ADD_TEST(suite, test_geo_mark_overlay); + + return suite; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mb_types.h Sat Jul 26 01:37:35 2008 +0800 @@ -0,0 +1,34 @@ +#ifndef __MB_TYPES_H_ +#define __MB_TYPES_H_ + +typedef float co_aix; + +/*! \brief A coordination system. + * + * It have a transform function defined by matrix to transform + * coordination from source space to target space. + * Source space is where the contained is drawed, and target space + * is where the coordination of parent container of the element + * represented by this coord object. + */ +typedef struct _coord { + int seq; + co_aix matrix[6]; + co_aix aggr_matrix[6]; + struct _coord *parent; + struct _coord *children, *sibling; +} coord_t; + + +typedef struct _geo geo_t; +typedef struct _shape { + int sh_type; + geo_t *geo; +} shape_t; + +enum { SHT_UNKNOW, SHT_PATH, SHT_TEXT }; + +extern void coord_init(coord_t *co, coord_t *parent); +extern void coord_trans_pos(coord_t *co, co_aix *x, co_aix *y); + +#endif /* __MB_TYPES_H_ */
--- /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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shapes.h Sat Jul 26 01:37:35 2008 +0800 @@ -0,0 +1,11 @@ +#ifndef __SHAPES_H_ +#define __SHAPES_H_ + +#include "mb_types.h" + +extern void sh_path_free(shape_t *path); +extern shape_t *sh_path_new(char *data); +extern void sh_path_transform(shape_t *shape, coord_t *coord); +extern void sh_path_draw(shape_t *shape, cairo_t *cr); + +#endif /* __SHAPES_H_ */
--- a/src/testcase.c Fri Jul 25 11:42:37 2008 +0800 +++ b/src/testcase.c Sat Jul 26 01:37:35 2008 +0800 @@ -1,6 +1,8 @@ #include <CUnit/Basic.h> extern CU_pSuite get_coord_suite(void); +extern CU_pSuite get_geo_suite(void); +extern CU_pSuite get_shape_path_suite(void); int main(int argc, char * const argv[]) { @@ -10,6 +12,8 @@ return CU_get_error(); get_coord_suite(); + get_geo_suite(); + get_shape_path_suite(); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests();