changeset 12:79e9edf4c00a

Add redraw manager
author Thinker K.F. Li <thinker@branda.to>
date Mon, 28 Jul 2008 17:45:36 +0800
parents 128af06c876c
children ed55009d96d3
files orgfiles/auto_update.muse src/Makefile src/X_main.c src/coord.c src/geo.c src/mb_types.h src/redraw_man.c src/redraw_man.h src/shape_path.c src/shapes.h src/testcase.c src/tools.c src/tools.h
diffstat 13 files changed, 773 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/orgfiles/auto_update.muse	Mon Jul 28 17:45:36 2008 +0800
@@ -0,0 +1,24 @@
+When an object change it's size, position, or components inside it,
+other objects overlaid with the object in old or new positions should
+be re-drawed.  The place that the object is resident in old or new
+position should be cleared before re-drawing.  We need a system
+to determine and track effected by the change of an object.
+
+The graphics are grouped into groups in tree-hierachy.  One group
+can be part of another group.  When a group's transformation matrix
+is chagned, all child groups should be affected.  All it's members
+should also be re-drawed.  All members of a group been changed are
+linked into update list in their order.  The one more close to
+tree root is more earier in the list.
+
+Transformations can only apply to groups.  A tree of coord_t is
+maintained to model relationships of groups.  Coord_t is coordination,
+a transfromation from user space to upper level's space or devcie space.
+
+coord_t must know graphics in respective group.  Once transformation
+matrix of a coord_t object changed, it can ask members to
+re-drawed them-self.
+
+Changes of the transformation of a coord_t should be propagated to
+child coord_t objects/groups.
+
--- a/src/Makefile	Sat Jul 26 06:34:15 2008 +0800
+++ b/src/Makefile	Mon Jul 28 17:45:36 2008 +0800
@@ -1,4 +1,4 @@
-SRCS =	coord.c geo.c shape_path.c
+SRCS =	coord.c geo.c shape_path.c redraw_man.c tools.c
 OBJS = ${SRCS:C/(.*)\.c/\1.o/g}
 TESTCASE_OBJS = ${SRCS:C/(.*)\.c/testcase-\1.o/g}
 CFLAGS =	-Wall -I/usr/local/include `pkg-config --cflags cairo`
--- a/src/X_main.c	Sat Jul 26 06:34:15 2008 +0800
+++ b/src/X_main.c	Mon Jul 28 17:45:36 2008 +0800
@@ -7,19 +7,24 @@
 
 #include <string.h>
 #include "shapes.h"
+#include "redraw_man.h"
 
 void draw_path(cairo_t *cr, int w, int h) {
+    redraw_man_t rdman;
     shape_t *path;
-    coord_t coord;
+    coord_t *coord;
+
+    redraw_man_init(&rdman);
+    coord = rdman.root_coord;
 
     path = sh_path_new("M 22,89.36218 C -34,-0.63782 39,-9.637817 82,12.36218 C 125,34.36218 142,136.36218 142,136.36218 C 100.66667,125.36218 74.26756,123.42795 22,89.36218 z ");
-    memset(coord.aggr_matrix, 0, sizeof(co_aix) * 6);
-    coord.aggr_matrix[0] = 0.8;
-    coord.aggr_matrix[1] = 0;
-    coord.aggr_matrix[2] = 20;
-    coord.aggr_matrix[4] = 0.8;
-    coord.aggr_matrix[5] = 20;
-    sh_path_transform(path, &coord);
+    coord->aggr_matrix[0] = 0.8;
+    coord->aggr_matrix[1] = 0;
+    coord->aggr_matrix[2] = 20;
+    coord->aggr_matrix[4] = 0.8;
+    coord->aggr_matrix[5] = 20;
+    rdman_add_shape(&rdman, (shape_t *)path, coord);
+    sh_path_transform(path);
     sh_path_draw(path, cr);
 }
 
@@ -33,8 +38,8 @@
     draw_path(cr, w, h);
     cairo_set_source_rgb(cr, 0.5, 0.9, 0.8);
     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");
+    cairo_set_font_size(cr, 36.0);
+    cairo_text_path(cr, "hello \xe6\xbc\xa2\xe5\xad\x97");
     cairo_set_line_width(cr, 2);
     cairo_stroke(cr);
 }
--- a/src/coord.c	Sat Jul 26 06:34:15 2008 +0800
+++ b/src/coord.c	Mon Jul 28 17:45:36 2008 +0800
@@ -6,6 +6,9 @@
 #include <string.h>
 #include "mb_types.h"
 
+
+#define ASSERT(x)
+
 /* To keep possibility of changing type of aix */
 #define MUL(a, b) ((a) * (b))
 #define ADD(a, b) ((a) + (b))
@@ -46,22 +49,22 @@
 
     visit = start;
     while(visit) {
-	child = visit->children;
+	child = STAILQ_HEAD(visit->children);
 	while(child) {
 	    compute_transform_function(child);
-	    child = child->sibling;
+	    child = STAILQ_NEXT(coord_t, sibling, child);
 	}
 
-	if(visit->children)
-	    visit = visit->children;
-	else if(visit->sibling)
-	    visit = visit->sibling;
+	if(STAILQ_HEAD(visit->children))
+	    visit = STAILQ_HEAD(visit->children);
+	else if(STAILQ_NEXT(coord_t, sibling, visit))
+	    visit = STAILQ_NEXT(coord_t, sibling, visit);
 	else {
 	    next = NULL;
 	    while(visit->parent && visit->parent != start) {
 		visit = visit->parent;
-		if(visit->sibling) {
-		    next = visit->sibling;
+		if(STAILQ_NEXT(coord_t, sibling, visit)) {
+		    next = STAILQ_NEXT(coord_t, sibling, visit);
 		    break;
 		}
 	    }
@@ -70,12 +73,17 @@
     }
 }
 
+/*! \brief Initialize a coord object.
+ *
+ * The object is cleared and matrix was initialized to ID.
+ * The object is be a children of specified parent.
+ */
 void coord_init(coord_t *co, coord_t *parent) {
     memset(co, 0, sizeof(coord_t));
     if(parent) {
+	/* insert at tail of children list. */
 	co->parent = parent;
-	co->sibling = parent->children;
-	parent->children = co;
+	STAILQ_INS_TAIL(parent->children, coord_t, sibling, co);
     }
     co->matrix[0] = 1;
     co->matrix[4] = 1;
@@ -94,6 +102,34 @@
     *y = ny;
 }
 
+coord_t *preorder_coord_tree(coord_t *last) {
+    coord_t *next;
+
+    ASSERT(last == NULL);
+    
+    if(STAILQ_HEAD(last->children))
+	next = STAILQ_HEAD(last->children);
+    else {
+	next = last;
+	while(next != NULL && STAILQ_NEXT(coord_t, sibling, next) == NULL)
+	    next = next->parent;
+	if(next)
+	    next = STAILQ_NEXT(coord_t, sibling, next);
+    }
+
+    return next;
+}
+
+void sh_attach_coord(shape_t *sh, coord_t *coord) {
+    STAILQ_INS_TAIL(coord->members, shape_t, coord_mem_next, sh);
+    sh->coord = coord;
+}
+
+void sh_detach_coord(shape_t *sh) {
+    STAILQ_REMOVE(sh->coord->members, shape_t, coord_mem_next, sh);
+    sh->coord = NULL;
+}
+
 #ifdef UNITTEST
 
 #include <CUnit/Basic.h>
@@ -157,11 +193,38 @@
     CU_ASSERT(y == 99);
 }
 
+void test_preorder_coord_tree(void) {
+    coord_t elms[6];
+    coord_t *last;
+
+    coord_init(elms, NULL);
+    coord_init(elms + 1, elms);
+    coord_init(elms + 2, elms);
+    coord_init(elms + 3, elms + 1);
+    coord_init(elms + 4, elms + 1);
+    coord_init(elms + 5, elms + 2);
+
+    last = elms;
+    last = preorder_coord_tree(last);
+    CU_ASSERT(last == elms + 1);
+    last = preorder_coord_tree(last);
+    CU_ASSERT(last == elms + 3);
+    last = preorder_coord_tree(last);
+    CU_ASSERT(last == elms + 4);
+    last = preorder_coord_tree(last);
+    CU_ASSERT(last == elms + 2);
+    last = preorder_coord_tree(last);
+    CU_ASSERT(last == elms + 5);
+    last = preorder_coord_tree(last);
+    CU_ASSERT(last == NULL);
+}
+
 CU_pSuite get_coord_suite(void) {
     CU_pSuite suite;
 
     suite = CU_add_suite("Suite_coord", NULL, NULL);
     CU_ADD_TEST(suite, test_update_aggr_matrix);
+    CU_ADD_TEST(suite, test_preorder_coord_tree);
 
     return suite;
 }
--- a/src/geo.c	Sat Jul 26 06:34:15 2008 +0800
+++ b/src/geo.c	Mon Jul 28 17:45:36 2008 +0800
@@ -7,15 +7,6 @@
 #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_scale_overlay(co_aix x1, co_aix w1, co_aix x2, co_aix w2) {
     if(x1 > x2) {
 	if((x1 - x2) >= w2)
@@ -27,7 +18,7 @@
     return 1;
 }
 
-static int is_overlay(geo_t *r1, geo_t *r2) {
+static int _is_overlay(geo_t *r1, geo_t *r2) {
     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))
@@ -36,6 +27,10 @@
     return 1;
 }
 
+int is_overlay(geo_t *r1, geo_t *r2) {
+    return _is_overlay(r1, r2);
+}
+
 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;
@@ -68,7 +63,7 @@
 
     ov_idx = 0;
     for(i = 0; i < n_others; i++) {
-	if(is_overlay(g, others[i]))
+	if(_is_overlay(g, others[i]))
 	    overlays[ov_idx++] = others[i];
     }
     *n_overlays = ov_idx;
--- a/src/mb_types.h	Sat Jul 26 06:34:15 2008 +0800
+++ b/src/mb_types.h	Mon Jul 28 17:45:36 2008 +0800
@@ -1,8 +1,11 @@
 #ifndef __MB_TYPES_H_
 #define __MB_TYPES_H_
 
+#include "tools.h"
+
 typedef float co_aix;
 typedef struct _shape shape_t;
+typedef struct _geo geo_t;
 
 /*! \brief A coordination system.
  *
@@ -11,28 +14,82 @@
  * 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.
+ *
+ * \dot
+ * digraph G {
+ * graph [rankdir=LR];
+ * root -> child00 -> child10 -> child20 [label="children" color="blue"];
+ * child00 -> child01 -> child02 [label="sibling"];
+ * child10 -> child11 [label="sibling"];
+ * }
+ * \enddot
  */
 typedef struct _coord {
-    int seq;
+    unsigned int seq;
     co_aix matrix[6];
     co_aix aggr_matrix[6];
     struct _coord *parent;
-    struct _coord *children, *sibling;
-    shape_t *members;
+    STAILQ(struct _coord) children;
+    struct _coord *sibling;
+    STAILQ(shape_t) members;	/*!< All shape objects in this coord. */
 } coord_t;
 
-
-typedef struct _geo geo_t;
-struct _shape {
-    int sh_type;
-    geo_t *geo;
-    struct _shape *sibling;
-};
-
-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);
 extern void update_aggr_matrix(coord_t *start);
+extern coord_t *preorder_coord_tree(coord_t *last);
+
+
+/*! \brief A grahpic shape.
+ *
+ * \dot
+ * digraph G {
+ * "shape" -> "coord";
+ * "shape" -> "geo";
+ * "geo" -> "shape";
+ * "coord" -> "shape" [label="members"]
+ * "shape" -> "shape" [label="sibling"];
+ * }
+ * \enddot
+ */
+struct _shape {
+    int sh_type;
+    geo_t *geo;
+    coord_t *coord;
+    shape_t *coord_mem_next;
+    struct _shape *sibling;
+};
+enum { SHT_UNKNOW, SHT_PATH, SHT_TEXT };
+
+#define sh_attach_geo(sh, g)			\
+    do {					\
+	(sh)->geo = g;				\
+	(g)->shape = (shape_t *)(sh);		\
+    } while(0)
+#define sh_detach_geo(sh)			\
+    do {					\
+	(sh)->geo->shape = NULL;		\
+	(sh)->geo = NULL;			\
+    } while(0)
+extern void sh_attach_coord(shape_t *sh, coord_t *coord);
+extern void sh_detach_coord(shape_t *sh);
+
+
+/*! \brief Geometry data of a shape or a group of shape.
+ */
+struct _geo {
+    shape_t *shape;
+    co_aix x, y;
+    co_aix w, h;
+    geo_t *next;		/*!< \brief Link geo_t objects. */
+    unsigned int seq;
+};
+
+extern int is_overlay(geo_t *r1, geo_t *r2);
+extern void geo_init(geo_t *g, int n_pos, co_aix pos[][2]);
+extern void geo_mark_overlay(geo_t *g, int n_others, geo_t **others,
+			     int *n_overlays, geo_t **overlays);
+#define geo_get_shape(g) ((g)->shape)
+#define geo_set_shape(g, sh) do {(g)->shape = sh;} while(0)
 
 #endif /* __MB_TYPES_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/redraw_man.c	Mon Jul 28 17:45:36 2008 +0800
@@ -0,0 +1,301 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mb_types.h"
+#include "shapes.h"
+#include "tools.h"
+#include "redraw_man.h"
+
+#define OK 0
+#define ERR -1
+
+int redraw_man_init(redraw_man_t *rdman) {
+    extern void redraw_man_destroy(redraw_man_t *rdman);
+
+    memset(rdman, 0, sizeof(redraw_man_t));
+
+    rdman->geo_pool = elmpool_new(sizeof(geo_t), 128);
+    if(rdman->geo_pool == NULL)
+	return ERR;
+
+    rdman->coord_pool = elmpool_new(sizeof(coord_t), 16);
+    if(rdman->coord_pool == NULL) {
+	elmpool_free(rdman->geo_pool);
+	return ERR;
+    }
+
+    rdman->root_coord = elmpool_elm_alloc(rdman->coord_pool);
+    if(rdman->root_coord == NULL)
+	redraw_man_destroy(rdman);
+
+    coord_init(rdman->root_coord, NULL);
+
+    return OK;
+}
+
+void redraw_man_destroy(redraw_man_t *rdman) {
+    elmpool_free(rdman->coord_pool);
+    elmpool_free(rdman->geo_pool);
+}
+
+
+#define ASSERT(x)
+/*
+ * Change transformation matrix
+ * - update aggregated transformation matrix
+ *   - of coord_t object been changed.
+ *   - of children coord_t objects.
+ * - redraw members of coord_t objects.
+ * - redraw shape objects they are overlaid with members.
+ *   - find out overlaid shape objects.
+ *   - geo_t of a coord_t object
+ *     - can make finding more efficiency.
+ *     - fill overlay geo_t objects of members.
+ *
+ * Change a shape object
+ * - redraw changed object.
+ * - redraw shape objects they are overlaid with changed object.
+ *   - find out overlaid shape objects.
+ *
+ * That coord and geo of shape objects are setted by user code
+ * give user code a chance to collect coord and geo objects together
+ * and gain interest of higher cache hit rate.
+ */
+
+/*! \brief Find out all affected shape objects.
+ *
+ * Find out all shape objects that are overalid with geo_t of
+ * a geometry changed object.
+ *
+ * Linear scan geo_t objects of all shape objects in all_shapes
+ * list of a redraw_man_t object.
+ */
+int rdman_find_overlaid_shapes(redraw_man_t *rdman, geo_t *geo,
+			 geo_t ***overlays) {
+    int n_geos;
+    geo_t **geos;
+    geo_t *geo_cur;
+    int n_overlays;
+    geo_t **_overlays;
+    int i;
+
+    n_geos = rdman->n_geos;
+
+    geos = (geo_t **)malloc(sizeof(geo_t *) * n_geos);
+    if(geos == NULL)
+	return -1;
+
+    _overlays = (geo_t **)malloc(sizeof(geo_t *) * n_geos);
+    if(geos == NULL) {
+	free(geos);
+	return -1;
+    }
+
+    geo_cur = STAILQ_HEAD(rdman->all_geos);
+    for(i = 0; i < n_geos; i++) {
+	geos[i] = geo_cur;
+	geo_cur = STAILQ_NEXT(geo_t, next, geo_cur);
+    }
+    geo_mark_overlay(geo, n_geos, geos, &n_overlays, _overlays);
+
+    free(geos);
+    *overlays = _overlays;
+
+    return n_overlays;
+}
+
+int rdman_add_shape(redraw_man_t *rdman, shape_t *shape, coord_t *coord) {
+    geo_t *geo;
+
+    geo = elmpool_elm_alloc(rdman->geo_pool);
+    if(geo == NULL)
+	return ERR;
+    sh_attach_geo(shape, geo);
+    STAILQ_INS_TAIL(rdman->all_geos, geo_t, next, geo);
+    rdman->n_geos++;
+    sh_attach_coord(shape, coord);
+
+    return OK;
+}
+
+int rdman_remove_shape(redraw_man_t *rdman, shape_t *shape) {
+    STAILQ_REMOVE(rdman->all_geos, geo_t, next, shape->geo);
+    elmpool_elm_free(rdman->geo_pool, shape->geo);
+    sh_detach_geo(shape);
+    rdman->n_geos--;
+    sh_detach_coord(shape);
+    return OK;
+}
+
+coord_t *rdman_coord_new(redraw_man_t *rdman, coord_t *parent) {
+    coord_t *coord;
+
+    coord = elmpool_elm_alloc(rdman->coord_pool);
+    if(coord == NULL)
+	return NULL;
+
+    coord_init(coord, parent);
+
+    return coord;
+}
+
+/*! \brief Free a coord of a redraw_man_t object.
+ *
+ * \param coord is a coord_t without children and members.
+ * \return 0 for successful, -1 for error.
+ */
+int rdman_coord_free(redraw_man_t *rdman, coord_t *coord) {
+    coord_t *parent;
+
+    parent = coord->parent;
+    if(parent == NULL)
+	return ERR;
+
+    if(STAILQ_HEAD(coord->members) != NULL)
+	return ERR;
+
+    if(STAILQ_HEAD(coord->children) != NULL)
+	return ERR;
+
+    STAILQ_REMOVE(parent->children, coord_t, sibling, coord);
+    elmpool_elm_free(rdman->coord_pool, coord);
+
+    return OK;
+}
+
+void rdman_coord_changed(redraw_man_t *rdman, coord_t *coord) {
+}
+
+/*
+ * When redraw an area, the affected elements may also extend to
+ * outside of the area.  Since the order of drawing will change
+ * the result, it will infect more and more elements to keep
+ * drawing order althrough they are overlaid directly with
+ * specified area.
+ *
+ * To fix the problem, we don't extend the set of redrawing to
+ * elements they are not overliad directly.  The redrawing is
+ * performed on a temporary surface, clipped to fit the area, and
+ * update only specified area on the destinate surface.
+ */
+
+/*
+ * To accelerate speed of transformation, when a matrix changed,
+ * transformation should be aggregated and computed in a loop.
+ * It can get intereset of higher hit rate of cache.
+ * - shapes prvoide list of positions needed to be transformed.
+ * - redraw_man transforms positions from shapes.
+ * - shapes drawing with result of transforms.
+ * - shapes should be called to give them a chance to update geometries.
+ */
+
+#ifdef UNITTEST
+
+#include <CUnit/Basic.h>
+
+struct _sh_dummy {
+    shape_t shape;
+    co_aix x, y;
+    co_aix w, h;
+};
+typedef struct _sh_dummy sh_dummy_t;
+
+shape_t *sh_dummy_new(co_aix x, co_aix y, co_aix w, co_aix h) {
+    sh_dummy_t *dummy;
+
+    dummy = (sh_dummy_t *)malloc(sizeof(sh_dummy_t));
+    if(dummy == NULL)
+	return NULL;
+
+    dummy->x = x;
+    dummy->y = y;
+    dummy->w = w;
+    dummy->h = h;
+
+    return (shape_t *)dummy;
+}
+
+void sh_dummy_free(shape_t *sh) {
+    free(sh);
+}
+
+void sh_dummy_transform(shape_t *shape) {
+    sh_dummy_t *dummy = (sh_dummy_t *)shape;
+    co_aix poses[2][2];
+    co_aix x1, y1, x2, y2;
+    
+    if(shape->geo && shape->coord) {
+	x1 = dummy->x;
+	y1 = dummy->y;
+	x2 = x1 + dummy->w;
+	y2 = y1 + dummy->h;
+
+	coord_trans_pos(shape->coord, &x1, &y1);
+	coord_trans_pos(shape->coord, &x2, &y2);
+	poses[0][0] = x1;
+	poses[0][1] = y1;
+	poses[1][0] = x2;
+	poses[1][1] = y2;
+    
+	geo_init(shape->geo, 2, poses);
+    }
+}
+
+void test_rdman_find_overlaid_shapes(void) {
+    redraw_man_t rdman;
+    geo_t geo;
+    coord_t *coords[3];
+    shape_t *shapes[5];
+    geo_t **overlays;
+    int n;
+    int i;
+
+    redraw_man_init(&rdman);
+    coords[0] = rdman.root_coord;
+    for(i = 1; i < 3; i++) {
+	coords[i] = rdman_coord_new(&rdman, rdman.root_coord);
+    }
+    for(i = 0; i < 5; i++) {
+	shapes[i] = sh_dummy_new(10 + i * 30, 10 + i * 20, 25, 15);
+	CU_ASSERT(shapes[i] != NULL);
+    }
+    for(i = 0; i < 3; i++)
+	rdman_add_shape(&rdman, shapes[i], coords[1]);
+    for(i = 3; i < 5; i++)
+	rdman_add_shape(&rdman, shapes[i], coords[2]);
+
+    coords[1]->matrix[0] = 2;
+    coords[0]->matrix[4] = 2;
+
+    update_aggr_matrix(coords[0]);
+    for(i = 0; i < 5; i++)
+	sh_dummy_transform(shapes[i]);
+
+    geo.x = 100;
+    geo.y = 120;
+    geo.w = 140;
+    geo.h = 40;
+
+    n = rdman_find_overlaid_shapes(&rdman, &geo, &overlays);
+    CU_ASSERT(n == 2);
+    CU_ASSERT(overlays != NULL);
+    CU_ASSERT(overlays[0] == shapes[2]->geo);
+    CU_ASSERT(overlays[1] == shapes[3]->geo);
+
+    free(overlays);
+    for(i = 0; i < 5; i++)
+	sh_dummy_free(shapes[i]);
+
+    redraw_man_destroy(&rdman);
+}
+
+CU_pSuite get_redraw_man_suite(void) {
+    CU_pSuite suite;
+
+    suite = CU_add_suite("Suite_redraw_man", NULL, NULL);
+    CU_ADD_TEST(suite, test_rdman_find_overlaid_shapes);
+
+    return suite;
+}
+
+#endif /* UNITTEST */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/redraw_man.h	Mon Jul 28 17:45:36 2008 +0800
@@ -0,0 +1,29 @@
+#ifndef __REDRAW_MAN_H_
+#define __REDRAW_MAN_H_
+
+#include "tools.h"
+#include "mb_types.h"
+
+/*! \brief Manage redrawing of shapes (graphic elements). */
+typedef struct _redraw_man {
+    int n_geos;
+    STAILQ(geo_t) all_geos;
+    coord_t *root_coord;
+    elmpool_t *geo_pool;
+    elmpool_t *coord_pool;
+    unsigned int seq;
+} redraw_man_t;
+
+extern int redraw_man_init(redraw_man_t *rdman);
+extern void redraw_man_destroy(redraw_man_t *rdman);
+extern int rdman_find_overlaid_shapes(redraw_man_t *rdman,
+				      geo_t *geo,
+				      geo_t ***overlays);
+extern int rdman_add_shape(redraw_man_t *rdman,
+			   shape_t *shape, coord_t *coord);
+extern int rdman_remove_shape(redraw_man_t *rdman, shape_t *shape);
+extern coord_t *rdman_coord_new(redraw_man_t *rdman, coord_t *parent);
+extern int rdman_coord_free(redraw_man_t *rdman, coord_t *coord);
+
+
+#endif /* __REDRAW_MAN_H_ */
--- a/src/shape_path.c	Sat Jul 26 06:34:15 2008 +0800
+++ b/src/shape_path.c	Mon Jul 28 17:45:36 2008 +0800
@@ -10,6 +10,10 @@
  * In user_data or dev_data, 0x00 bytes are padding after commands.
  * No commands other than 0x00 can resident after 0x00 itself.
  * It means command processing code can skip commands after a 0x00.
+ *
+ * Shapes should check if shape_t::geo is assigned.  Once transformation
+ * matrics are changed, shape objects should update shape_t::geo if
+ * it is assigned.
  */
 typedef struct _sh_path {
     shape_t shape;
@@ -352,6 +356,7 @@
     cmd_cnt = (cmd_cnt + 3) & ~0x3;
 
     path = (sh_path_t *)malloc(sizeof(sh_path_t));
+    memset(&path->shape, 0, sizeof(shape_t));
     path->shape.sh_type = SHT_PATH;
     path->cmd_len = cmd_cnt;
     path->arg_len = arg_cnt;
@@ -385,9 +390,11 @@
  * TODO: associate coord_t with shape objects and transform them
  *       automatically.
  */
-void sh_path_transform(shape_t *shape, coord_t *coord) {
+void sh_path_transform(shape_t *shape) {
     sh_path_t *path;
     co_aix *user_args, *dev_args;
+    co_aix (*poses)[2];
+    int arg_len;
     int i;
 
     ASSERT(shape->type == SHT_PATH);
@@ -396,12 +403,18 @@
     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) {
+    arg_len = path->arg_len;
+    for(i = 0; i < arg_len; i += 2) {
 	dev_args[0] = *user_args++;
 	dev_args[1] = *user_args++;
-	coord_trans_pos(coord, dev_args, dev_args + 1);
+	coord_trans_pos(shape->coord, dev_args, dev_args + 1);
 	dev_args += 2;
     }
+
+    if(path->shape.geo) {
+	poses = (co_aix (*)[2])(path->dev_data + path->cmd_len);
+	geo_init(path->shape.geo, arg_len / 2, poses);
+    }
 }
 
 void sh_path_draw(shape_t *shape, cairo_t *cr) {
@@ -562,6 +575,7 @@
     sh_path_t *path;
     co_aix *args;
     coord_t coord;
+    geo_t geo;
 
     path = (sh_path_t *)sh_path_new("M 33 25l33 55C 33 87 44 22 55 99L33 77z");
     CU_ASSERT(path != NULL);
@@ -570,13 +584,17 @@
     CU_ASSERT(strcmp(path->user_data, "MLCLZ") == 0);
     CU_ASSERT(strcmp(path->dev_data, "MLCLZ") == 0);
 
+    path->shape.geo = &geo;
+    geo.shape = (shape_t *)path;
+
     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);
+    path->shape.coord = &coord;
+    sh_path_transform((shape_t *)path);
 
     args = (co_aix *)(path->dev_data + path->cmd_len);
     CU_ASSERT(args[0] == 34);
@@ -591,6 +609,7 @@
     CU_ASSERT(args[9] == 198);
     CU_ASSERT(args[10] == 34);
     CU_ASSERT(args[11] == 154);
+
     sh_path_free((shape_t *)path);
 }
 
--- a/src/shapes.h	Sat Jul 26 06:34:15 2008 +0800
+++ b/src/shapes.h	Mon Jul 28 17:45:36 2008 +0800
@@ -1,11 +1,12 @@
 #ifndef __SHAPES_H_
 #define __SHAPES_H_
 
+#include <cairo.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_transform(shape_t *shape);
 extern void sh_path_draw(shape_t *shape, cairo_t *cr);
 
 #endif /* __SHAPES_H_ */
--- a/src/testcase.c	Sat Jul 26 06:34:15 2008 +0800
+++ b/src/testcase.c	Mon Jul 28 17:45:36 2008 +0800
@@ -1,8 +1,10 @@
 #include <CUnit/Basic.h>
 
+extern CU_pSuite get_tools_suite(void);
 extern CU_pSuite get_coord_suite(void);
 extern CU_pSuite get_geo_suite(void);
 extern CU_pSuite get_shape_path_suite(void);
+extern CU_pSuite get_redraw_man_suite(void);
 
 int
 main(int argc, char * const argv[]) {
@@ -11,9 +13,21 @@
     if(CU_initialize_registry() != CUE_SUCCESS)
 	return CU_get_error();
 
-    get_coord_suite();
-    get_geo_suite();
-    get_shape_path_suite();
+    suite = get_tools_suite();
+    if(suite == NULL)
+	return CU_get_error();
+    suite = get_coord_suite();
+    if(suite == NULL)
+	return CU_get_error();
+    suite = get_geo_suite();
+    if(suite == NULL)
+	return CU_get_error();
+    suite = get_shape_path_suite();
+    if(suite == NULL)
+	return CU_get_error();
+    suite = get_redraw_man_suite();
+    if(suite == NULL)
+	return CU_get_error();
 
     CU_basic_set_mode(CU_BRM_VERBOSE);
     CU_basic_run_tests();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools.c	Mon Jul 28 17:45:36 2008 +0800
@@ -0,0 +1,152 @@
+#include <stdlib.h>
+#include "tools.h"
+
+
+/*! \brief Small fixed size data elements management.
+ *
+ * It is used to management a large number of data elements
+ * they with the same memory size, a fixed size.  It allocate
+ * a large block for a lot of elements a time for more efficiency
+ * utilization of memory.
+ *
+ * Elements with the size and in a large number usually be accessed
+ * very close in time.  Allocate a large block for elements also
+ * increase cache hit rate.
+ *
+ * Blocks are keep track as a linking list.  They are freed when
+ * the elmpool_t is freed.  It costs overhead of size of a element
+ * for each block.  We use memory of first element of blocks to
+ * be next pointer of linking list.  So, it can not be used by user
+ * code.
+ */
+struct _elmpool {
+    int elm_sz;
+    int inc_num;
+    void *frees;
+    void *blks;			/* list of allocated blocks. */
+};
+
+/*! \brief Create a new data elements pool.
+ *
+ * elmpool_t provide a pool of fixed size elements to gain better
+ * utilization of memory.  It try to allocate bigger memory blocks
+ * for multiple elements.
+ *
+ * \param elm_sz size of elements.
+ * \param inc_num is number of elments to allocate every time. (>= 16)
+ * \return A elmpool or NULL for error.
+ */
+elmpool_t *elmpool_new(int elm_sz, int inc_num) {
+    int _elm_sz;
+    elmpool_t *pool;
+
+    if(inc_num < 16)
+	return NULL;
+
+    if(elm_sz >= sizeof(void *))
+	_elm_sz = elm_sz;
+    else
+	_elm_sz = sizeof(void *);
+
+    pool = (elmpool_t *)malloc(sizeof(elmpool_t));
+    if(pool == NULL)
+	return NULL;
+
+    pool->elm_sz = _elm_sz;
+    if(inc_num == 0)
+	inc_num = 256;
+    pool->inc_num = inc_num;
+    pool->frees = NULL;
+    pool->blks = NULL;
+
+    return pool;
+}
+
+void *elmpool_elm_alloc(elmpool_t *pool) {
+    void *blk, *elm;
+    int elm_sz, inc_num;
+    int i;
+
+    if(pool->frees == NULL) {
+	inc_num = pool->inc_num;
+	elm_sz = pool->elm_sz;
+	blk = malloc(elm_sz * inc_num);
+	if(blk == NULL)
+	    return NULL;
+
+	*(void **)blk = pool->blks;
+	pool->blks = blk;
+
+	blk = blk + elm_sz;
+	pool->frees = blk;
+	for(i = 2; i < inc_num; i++) {
+	    *(void **)blk = blk + elm_sz;
+	    blk = *(void **)blk;
+	}
+	*(void **)blk = NULL;
+    }
+
+    elm = pool->frees;
+    pool->frees = *(void **)elm;
+
+    return elm;
+}
+
+void elmpool_elm_free(elmpool_t *pool, void *elm) {
+    *(void **)elm = pool->frees;
+    pool->frees = elm;
+}
+
+void elmpool_free(elmpool_t *pool) {
+    void *blk, *next_blk;
+
+    blk = pool->blks;
+    while(blk) {
+	next_blk = *(void **)blk;
+	free(blk);
+	blk = next_blk;
+    }
+}
+
+#ifdef UNITTEST
+
+#include <CUnit/Basic.h>
+
+void test_elmpool(void) {
+    elmpool_t *pool;
+    void *elm;
+    int i;
+
+    pool = elmpool_new(64, 16);
+    for(i = 0; i < 15; i++) {
+	elm = elmpool_elm_alloc(pool);
+	CU_ASSERT(elm != NULL);
+    }
+    CU_ASSERT(pool->frees == NULL);
+
+    for(i = 0; i < 15; i++) {
+	elm = elmpool_elm_alloc(pool);
+	CU_ASSERT(elm != NULL);
+    }
+    CU_ASSERT(pool->frees == NULL);
+
+    elmpool_elm_free(pool, elm);
+    CU_ASSERT(pool->frees == elm);
+
+    elm = elmpool_elm_alloc(pool);
+    CU_ASSERT(elm != NULL);
+    CU_ASSERT(pool->frees == NULL);
+
+    elmpool_free(pool);
+}
+
+CU_pSuite get_tools_suite(void) {
+    CU_pSuite suite;
+
+    suite = CU_add_suite("Suite_tools", NULL, NULL);
+    CU_ADD_TEST(suite, test_elmpool);
+
+    return suite;
+}
+
+#endif /* UNITTEST */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools.h	Mon Jul 28 17:45:36 2008 +0800
@@ -0,0 +1,60 @@
+#ifndef __TOOLS_H_
+#define __TOOLS_H_
+
+
+typedef struct _elmpool elmpool_t;
+
+extern elmpool_t *elmpool_new(int elm_sz, int inc_num);
+extern void *elmpool_elm_alloc(elmpool_t *pool);
+extern void elmpool_elm_free(elmpool_t *pool, void *elm);
+extern void elmpool_free(elmpool_t *pool);
+
+
+#define STAILQ(type)				\
+    struct {					\
+	type *head;				\
+	type *tail;				\
+    }
+#define STAILQ_INIT(q)				\
+    do {					\
+	(q).head = (q).tail = NULL;		\
+    } while(0)
+#define STAILQ_HEAD(q) ((q).head)
+#define STAILQ_TAIL(q) ((q).tail)
+#define STAILQ_NEXT(type, field, elm) ((elm)->field)
+#define STAILQ_INS(q, type, field, elm)		\
+    do {					\
+	(elm)->field = (q).head;		\
+	(q).head = elm;				\
+	if((q).tail == NULL)			\
+	    (q).tail = elm;			\
+    } while(0)
+#define STAILQ_INS_TAIL(q, type, field, elm)	\
+    do {					\
+	(elm)->field = NULL;			\
+	if((q).tail != NULL)			\
+	    (q).tail->field = elm;		\
+	(q).tail = elm;				\
+	if((q).head == NULL)			\
+	    (q).head = elm;			\
+    } while(0)
+#define STAILQ_REMOVE(q, type, field, elm)		\
+    do {						\
+	if((elm) == (q).head) {				\
+	    (q).head = (elm)->field;			\
+	    if((q).head == NULL)			\
+		(q).tail = NULL;			\
+	} else {					\
+	    type *_stailq_cur = (q).head;		\
+	    while(_stailq_cur != NULL &&		\
+		  _stailq_cur->field != (elm))		\
+		_stailq_cur = _stailq_cur->field;	\
+	    if(_stailq_cur != NULL) {			\
+		_stailq_cur->field = elm;		\
+		if((q).tail == (elm))			\
+		    (q).tail = _stailq_cur;		\
+	    }						\
+	}						\
+    } while(0)
+
+#endif /* __TOOLS_H_ */