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();