changeset 319:1633b5aadfc3

Merge the result
author wycc
date Thu, 05 Mar 2009 07:54:35 +0800
parents 4b58e58c78da (current diff) d0f8642d3508 (diff)
children 6e164a9dd46c
files
diffstat 8 files changed, 1109 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/examples/menu/Makefile.am	Thu Mar 05 07:53:58 2009 +0800
+++ b/examples/menu/Makefile.am	Thu Mar 05 07:54:35 2009 +0800
@@ -2,25 +2,37 @@
 
 #SUFFIXES=.svg .so
 
-list.so:list.svg
-	$(top_srcdir)/tools/svg2code.py $< $<.mb
-	m4 -I $(top_srcdir)/tools mb_c_source.m4 $<.mb > $(<:.svg=.c)
-	m4 -I $(top_srcdir)/tools mb_c_header.m4 $<.mb > $(<:.svg=.h)
+list.so:list.c list.h
 	$(MAKE) $(<:.svg=.o)
 	$(CC) -shared -o $@ $(<:.svg=.o)
 
-browser.so:browser.svg
+list.mb: $(srcdir)/list.svg
 	$(top_srcdir)/tools/svg2code.py $< $<.mb
+
+list.c: list.mb list.h
 	m4 -I $(top_srcdir)/tools mb_c_source.m4 $<.mb > $(<:.svg=.c)
+
+list.h: list.mb
 	m4 -I $(top_srcdir)/tools mb_c_header.m4 $<.mb > $(<:.svg=.h)
+
+browser.so: browser.c browser.h
 	$(MAKE) $(<:.svg=.o)
 	$(CC) -shared -o $@ $(<:.svg=.o)
 
+browser.h: browser.mb
+	m4 -I $(top_srcdir)/tools mb_c_header.m4 $<.mb > $(<:.svg=.h)
+
+browser.c: browser.mb browser.h
+	m4 -I $(top_srcdir)/tools mb_c_source.m4 $<.mb > $(<:.svg=.c)
+
+browser.mb: $(srcdir)/browser.svg
+	$(top_srcdir)/tools/svg2code.py $< $<.mb
+
 noinst_PROGRAMS = menu filebrowser list.so browser.so
 EXTRA_DIST = 
 
 menu_SOURCES = main.c animated_menu.c animated_menu.h
-nodist_dynamic_SOURCES = 
+nodist_menu_SOURCES = 
 CFLAGS = @pangocairo_CFLAGS@ 
 menu_CFLAGS = @pangocairo_CFLAGS@ 
 menu_LDFLAGS = @pangocairo_LIBS@ 
--- a/include/mb_redraw_man.h	Thu Mar 05 07:53:58 2009 +0800
+++ b/include/mb_redraw_man.h	Thu Mar 05 07:54:35 2009 +0800
@@ -23,7 +23,6 @@
 
 DARRAY(coords, coord_t *);
 DARRAY(geos, geo_t *);
-DARRAY(areas, area_t *);
 
 /*! \brief Manage redrawing of shapes (graphic elements).
  *
@@ -52,12 +51,14 @@
     elmpool_t *subject_pool;
     elmpool_t *paint_color_pool;
     elmpool_t *pent_pool;
+    elmpool_t *coord_canvas_pool;
 
     coords_t dirty_coords;
     geos_t dirty_geos;
-    areas_t dirty_areas;
+    int n_dirty_areas;		/*!< \brief Number of all dirty areas. */
 
     geos_t gen_geos;
+    coords_t zeroing_coords;
 
     STAILQ(shape_t) shapes;	/*!< \brief All managed shapes.  */
     STAILQ(paint_t) paints;	/*!< \brief All managed paints. */
--- a/include/mb_types.h	Thu Mar 05 07:53:58 2009 +0800
+++ b/include/mb_types.h	Thu Mar 05 07:54:35 2009 +0800
@@ -14,6 +14,8 @@
 typedef struct _paint paint_t;
 typedef struct _mb_obj mb_obj_t;
 typedef struct _mb_sprite mb_sprite_t;
+/*! \todo Replace cairo_t with canvas_t. */
+typedef cairo_t canvas_t;
 
 struct _redraw_man;
 
@@ -133,7 +135,26 @@
 #define geo_get_flags(g, mask) ((g)->flags & (mask))
 #define geo_set_flags(g, mask) do {(g)->flags |= mask;} while(0)
 #define geo_clear_flags(g, mask) do {(g)->flags &= ~(mask);} while(0)
+#define geo_get_coord(g) sh_get_coord(geo_get_shape(g))
 
+/*! \defgroup coord Coordination
+ * @{
+ */
+typedef struct _coord coord_t;
+
+DARRAY(areas, area_t *);
+
+/*! \brief Canvas information for a coord.
+ */
+typedef struct _coord_canvas_info {
+    coord_t *owner;		/*!< Cached one or opacity == 1 */
+    canvas_t *canvas;
+    areas_t dirty_areas;	/*!< \brief Areas should be updated
+				 * in canvas.
+				 */
+    area_t aggr_dirty_areas[2];	/*!< Used to aggregate updates to parent. */
+    area_t cached_dirty_area;	/*!< Used to dirty an area in cached space. */
+} coord_canvas_info_t;
 
 /*! \brief A coordination system.
  *
@@ -152,10 +173,10 @@
  * }
  * \enddot
  */
-typedef struct _coord {
+struct _coord {
     mb_obj_t obj;
     unsigned int order;
-    unsigned int flags;
+    unsigned int flags;		/*!< \sa \ref coord_flags */
     co_aix opacity;
     /*! Own one or inherit from an ancestor.
      * Setup it when clean coords.
@@ -163,7 +184,7 @@
      * - \ref COF_OWN_CANVAS
      * - \ref redraw
      */
-    cairo_t *canvas;
+    coord_canvas_info_t *canvas_info;
     area_t *cur_area, *last_area;
     area_t areas[2];
 
@@ -180,7 +201,10 @@
     STAILQ(geo_t) members;	/*!< \brief All geo_t members in this coord. */
 
     subject_t *mouse_event;
-} coord_t;
+};
+/*! \defgroup coord_flags Coord Flags
+ * @{
+ */
 #define COF_DIRTY 0x1
 #define COF_HIDDEN 0x2	        /*!< A coord is hidden. */
 #define COF_OWN_CANVAS 0x4	/*!< A coord owns a canvas or inherit it
@@ -190,11 +214,33 @@
 				 * when trivaling.
 				 */
 #define COF_FREE 0x10
+#define COF_FAST_CACHE 0x20	/*!< \brief Cache raster image in fast way.
+				 * \sa \ref img_cache
+				 */
+#define COF_PRECISE_CACHE 0x40	/*!< \brief Cache raster image in
+				 * precise way.
+				 * \sa \ref img_cache
+				 */
+#define COF_CACHE_MASK 0x60
+#define COF_ANCESTOR_CACHE 0x80	/*!< \brief One ancestor is cached.
+				 * \sa \ref img_cache
+				 */
+#define COF_MUST_ZEROING 0x100	/*!< \sa \ref cache_imp */
+#define COF_JUST_CLEAN 0x200	/*!< \brief This coord is just cleaned by
+				 *    last clean.
+				 */
+#define COF_TEMP_MARK 0x400	/*!< \brief Temporary mark a coord. */
+/* @} */
+
+extern void matrix_mul(co_aix *m1, co_aix *m2, co_aix *dst);
+extern void matrix_trans_pos(co_aix *matrix, co_aix *x, co_aix *y);
 
 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 co_aix coord_trans_size(coord_t *co, co_aix size);
 extern void compute_aggr_of_coord(coord_t *coord);
+extern void compute_aggr_of_cached_coord(coord_t *coord);
+extern void compute_reverse(co_aix *orig, co_aix *reverse);
 extern void update_aggr_matrix(coord_t *start);
 extern coord_t *preorder_coord_subtree(coord_t *root, coord_t *last);
 extern coord_t *postorder_coord_subtree(coord_t *root, coord_t *last);
@@ -205,6 +251,34 @@
 	(co)->flags |= COF_HIDDEN;    \
     } while(0)
 #define coord_show(co) do { co->flags &= ~COF_HIDDEN; } while(0)
+#define coord_fast_cache(co)					\
+    do {							\
+	(co)->flags =						\
+	    ((co)->flags & ~COF_CACHE_MASK) | COF_FAST_CACHE;	\
+    } while(0)
+#define coord_precise_cache(co)						\
+    do {								\
+	(co)->flags =							\
+	    ((co)->flags & ~COF_CACHE_MASK) | COF_PRECISE_CACHE;	\
+    } while(0)
+#define coord_nocache(co)					\
+    do {							\
+	(co)->flags &= ~COF_CACHE_MASK;				\
+    } while(0)
+#define coord_is_root(co) ((co)->parent == NULL)
+#define coord_is_cached(co) ((co)->flags & COF_CACHE_MASK)
+#define coord_is_fast_cached(co) ((co)->flags & COF_FAST_MASK)
+#define coord_is_precise_cached(co) ((co)->flags & COF_PRECISE_MASK)
+#define coord_is_zeroing(co) ((co)->flags & COF_MUST_ZEROING)
+#define coord_set_zeroing(co) \
+    do { (co)->flags |= COF_MUST_ZEROING; } while(0)
+#define coord_clear_zeroing(co) \
+    do { (co)->flags &= ~COF_MUST_ZEROING; } while(0)
+#define coord_set_flags(co, _flags)		\
+    do { (co)->flags |= (_flags); } while(0)
+#define coord_get_flags(co, _flags) ((co)->flags & (_flags))
+#define coord_clear_flags(co, _flags)		\
+    do { (co)->flags &= ~(_flags); } while(0)
 #define coord_get_mouse_event(coord) ((coord)->mouse_event)
 #define coord_get_aggr_matrix(coord) ((coord)->aggr_matrix)
 #define FOR_COORDS_POSTORDER(coord, cur)			\
@@ -238,6 +312,14 @@
 	shape = geo_get_shape_safe(STAILQ_NEXT(geo_t, coord_next,	\
 					       sh_get_geo(shape))))
 #define coord_get_area(coord) ((coord)->cur_area)
+#define _coord_get_canvas(coord) ((coord)->canvas_info->canvas)
+#define _coord_set_canvas(coord, _canvas)		\
+    do {						\
+	(coord)->canvas_info->canvas = _canvas;		\
+    } while(0)
+#define _coord_get_dirty_areas(coord) (&(coord)->canvas_info->dirty_areas)
+
+/* @} */
 
 /*! \brief A grahpic shape.
  *
--- a/src/coord.c	Thu Mar 05 07:53:58 2009 +0800
+++ b/src/coord.c	Thu Mar 05 07:54:35 2009 +0800
@@ -25,7 +25,39 @@
     dst[5] = ADD(ADD(MUL(m1[3], m2[2]), MUL(m1[4], m2[5])), m1[5]);
 }
 
-/*! \brief Compute agrregated transform function.
+void matrix_mul(co_aix *m1, co_aix *m2, co_aix *dst) {
+    co_aix *_dst = dst;
+    co_aix fake_dst[6];
+    
+    if(m1 == dst || m2 == dst)
+	_dst = fake_dst;
+    
+    mul_matrix(m1, m2, _dst);
+
+    if(m1 == dst || m2 == dst) {
+	dst[0] = fake_dst[0];
+	dst[1] = fake_dst[1];
+	dst[2] = fake_dst[2];
+	dst[3] = fake_dst[3];
+	dst[4] = fake_dst[4];
+	dst[5] = fake_dst[5];
+    }
+}
+
+void matrix_trans_pos(co_aix *matrix, co_aix *x, co_aix *y) {
+    co_aix nx, ny;
+
+    nx = ADD(ADD(MUL(matrix[0], *x),
+		 MUL(matrix[1], *y)),
+	     matrix[2]);
+    ny = ADD(ADD(MUL(matrix[3], *x),
+		 MUL(matrix[4], *y)),
+	     matrix[5]);
+    *x = nx;
+    *y = ny;
+}
+
+/*! \brief Compute aggregated transform matrix.
  *
  * Base on parent's aggregated matrix if it is existed, or use transform
  * matrix as aggregated matrix. 
@@ -42,10 +74,83 @@
     compute_transform_function(coord);
 }
 
+/*! \brief Compute aggregated transform matrix for cached coord.
+ *
+ * \sa \ref img_cache
+ */
+static void compute_transform_function_cached(coord_t *visit) {
+    co_aix *p_matrix;
+    co_aix cache_p_matrix[6];
+    co_aix cache_scale_x, cache_scale_y;
+    
+    if(visit->parent) {
+	p_matrix = coord_get_aggr_matrix(visit->parent);
+	cache_scale_x =
+	    sqrtf(p_matrix[0] * p_matrix[0] + p_matrix[3] * p_matrix[3]);
+	cache_scale_y =
+	    sqrtf(p_matrix[1] * p_matrix[1] + p_matrix[4] * p_matrix[4]);
+	cache_p_matrix[0] = cache_scale_x;
+	cache_p_matrix[1] = 0;
+	cache_p_matrix[2] = 0;
+	cache_p_matrix[3] = 0;
+	cache_p_matrix[4] = cache_scale_y;
+	cache_p_matrix[5] = 0;
+	mul_matrix(cache_p_matrix, visit->matrix, visit->aggr_matrix);
+    } else {
+	memcpy(visit->aggr_matrix, visit->matrix, sizeof(visit->matrix));
+    }
+}
+
+void compute_aggr_of_cached_coord(coord_t *coord) {
+    compute_transform_function_cached(coord);
+}
+
+void compute_reverse(co_aix *orig, co_aix *reverse) {
+    co_aix working[6];
+    co_aix factor;
+    
+#define VEC_MAC(src, factor, dst)		\
+    do {					\
+	(dst)[0] += (src)[0] * (factor);	\
+	(dst)[1] += (src)[1] * (factor);	\
+	(dst)[2] += (src)[2] * (factor);	\
+    } while(0)
+
+    reverse[0] = 1;
+    reverse[1] = 0;
+    reverse[2] = 0;
+    reverse[3] = 0;
+    reverse[4] = 1;
+    reverse[5] = 0;
+    
+    memcpy(working, orig, sizeof(co_aix) * 6);
+
+    factor = -working[3] / working[0];
+    VEC_MAC(working, factor, working + 3);
+    VEC_MAC(reverse, factor, reverse + 3);
+
+    factor = -working[1] / working[4];
+    VEC_MAC(working + 3, factor, working);
+    VEC_MAC(reverse + 3, factor, reverse);
+
+    reverse[2] = -working[2];
+    reverse[5] = -working[5];
+
+    reverse[0] /= working[0];
+    reverse[1] /= working[0];
+    reverse[2] /= working[0];
+    reverse[3] /= working[4];
+    reverse[4] /= working[4];
+    reverse[5] /= working[4];
+}
+
 /*! \brief Update aggregate matrices of elements under a sub-tree.
  *
  * A subtree is specified by the root of it.  All elements in the subtree
  * are effected by that changes of matrix of the subtree root.
+ *
+ * \todo Remove update_aggr_matrix() since it is out of date and
+ *	no one use it.
  */
 void update_aggr_matrix(coord_t *start) {
     coord_t *visit, *child, *next;
--- a/src/event.c	Thu Mar 05 07:53:58 2009 +0800
+++ b/src/event.c	Thu Mar 05 07:54:35 2009 +0800
@@ -257,6 +257,9 @@
 coord_t *preorder_coord_subtree(coord_t *root, coord_t *last) {
     if(STAILQ_HEAD(last->children) && !(last->flags & COF_SKIP))
 	return STAILQ_HEAD(last->children);
+    
+    last->flags &= ~COF_SKIP;
+    
     if(last == root)
 	return NULL;
     while(STAILQ_NEXT(coord_t, sibling, last) == NULL) {
@@ -269,7 +272,7 @@
 
 static
 void preorder_coord_skip_subtree(coord_t *coord) {
-    coord->flags &= ~COF_SKIP;
+    coord->flags |= COF_SKIP;
 }
 
 static
--- a/src/mouse.c	Thu Mar 05 07:53:58 2009 +0800
+++ b/src/mouse.c	Thu Mar 05 07:54:35 2009 +0800
@@ -63,7 +63,7 @@
     int cnt = 0;
     
     mevt = (monitor_event_t *)evt;
-    rdman = (redraw_man_t *)evt->tgt;
+    rdman = (redraw_man_t *)arg;
     obj = (mb_obj_t *)subject_get_object(mevt->subject);
     props = mb_obj_prop_store(obj);
 
--- a/src/redraw_man.c	Thu Mar 05 07:53:58 2009 +0800
+++ b/src/redraw_man.c	Thu Mar 05 07:54:35 2009 +0800
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 #include <cairo.h>
 #include "mb_types.h"
 #include "mb_shapes.h"
@@ -31,8 +32,7 @@
 #define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0)
 #define rdman_is_dirty(rdman)			\
     ((rdman)->dirty_coords.num != 0 ||		\
-     (rdman)->dirty_geos.num != 0 ||		\
-     (rdman)->dirty_areas.num != 0)
+     (rdman)->dirty_geos.num != 0)
 
 #define OK 0
 #define ERR -1
@@ -132,15 +132,27 @@
 static int add_dirty_coord(redraw_man_t *rdman, coord_t *coord) {
     coord->flags |= COF_DIRTY;
     ADD_DATA(coords, dirty_coords, coord);
+    return OK;
 }
 
 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
     geo->flags |= GEF_DIRTY;
     ADD_DATA(geos, dirty_geos, geo);
+    return OK;
 }
 
-static int add_dirty_area(redraw_man_t *rdman, area_t *area) {
-    ADD_DATA(areas, dirty_areas, area);
+static int add_dirty_area(redraw_man_t *rdman, coord_t *coord, area_t *area) {
+    int r;
+    
+    rdman->n_dirty_areas++;
+    r = areas_add(_coord_get_dirty_areas(coord), area);
+    return r == 0? OK: ERR;
+}
+
+static int add_zeroing_coord(redraw_man_t *rdman, coord_t *coord) {
+    coord_set_zeroing(coord);
+    ADD_DATA(coords, zeroing_coords, coord);
+    return OK;
 }
 
 static int add_free_obj(redraw_man_t *rdman, void *obj,
@@ -188,15 +200,11 @@
     poses[1][1] = area->y + area->h;;
 }
 
-static cairo_t *new_canvas(redraw_man_t *rdman) {
+static cairo_t *canvas_new(int w, int h) {
 #ifndef UNITTEST
+    cairo_surface_t *surface;
     cairo_t *cr;
-    cairo_surface_t *surface, *cr_surface;
-    int w, h;
-
-    cr_surface = cairo_get_target(rdman->cr);
-    w = cairo_image_surface_get_width(cr_surface);
-    h = cairo_image_surface_get_height(cr_surface);
+    
     surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
 					 w, h);
     cr = cairo_create(surface);
@@ -207,12 +215,25 @@
 #endif
 }
 
-static void free_canvas(cairo_t *canvas) {
+static void canvas_free(cairo_t *canvas) {
 #ifndef UNITTEST
     cairo_destroy(canvas);
 #endif
 }
 
+static void canvas_get_size(cairo_t *canvas, int *w, int *h) {
+#ifndef UNITTEST
+    cairo_surface_t *surface;
+
+    surface = cairo_get_target(canvas);
+    *w = cairo_image_surface_get_width(surface);
+    *h = cairo_image_surface_get_height(surface);
+#else
+    *w = 0;
+    *h = 0;
+#endif
+}
+
 static int geo_off_in_coord(geo_t *geo, coord_t *coord) {
     int off = 0;
     geo_t *vgeo;
@@ -246,6 +267,28 @@
     coord->num_members--;
 }
 
+static coord_canvas_info_t *coord_canvas_info_new(redraw_man_t *rdman,
+						  coord_t *coord,
+						  cairo_t *canvas) {
+    coord_canvas_info_t *info;
+
+    info = (coord_canvas_info_t *)elmpool_elm_alloc(rdman->coord_canvas_pool);
+    if(info == NULL)
+	return info;
+    
+    info->owner = coord;
+    info->canvas = canvas;
+    DARRAY_INIT(&info->dirty_areas);
+
+    return info;
+}
+
+static void coord_canvas_info_free(redraw_man_t *rdman,
+				   coord_canvas_info_t *info) {
+    DARRAY_DESTROY(&info->dirty_areas);
+    elmpool_elm_free(rdman->coord_canvas_pool, info);
+}
+
 static void mouse_event_root_dummy(event_t *evt, void *arg) {
 }
 
@@ -257,6 +300,11 @@
 
     memset(rdman, 0, sizeof(redraw_man_t));
 
+    DARRAY_INIT(&rdman->dirty_coords);
+    DARRAY_INIT(&rdman->dirty_geos);
+    DARRAY_INIT(&rdman->gen_geos);
+    DARRAY_INIT(&rdman->zeroing_coords);
+    
     rdman->geo_pool = elmpool_new(sizeof(geo_t), 128);
     rdman->coord_pool = elmpool_new(sizeof(coord_t), 16);
     rdman->shnode_pool = elmpool_new(sizeof(shnode_t), 16);
@@ -264,9 +312,10 @@
     rdman->subject_pool = elmpool_new(sizeof(subject_t), 32);
     rdman->paint_color_pool = elmpool_new(_paint_color_size, 64);
     rdman->pent_pool = elmpool_new(sizeof(mb_prop_entry_t), 128);
+    rdman->coord_canvas_pool = elmpool_new(sizeof(coord_canvas_info_t), 16);
     if(!(rdman->geo_pool && rdman->coord_pool && rdman->shnode_pool &&
 	 rdman->observer_pool && rdman->subject_pool &&
-	 rdman->paint_color_pool))
+	 rdman->paint_color_pool && rdman->coord_canvas_pool))
 	goto err;
 
     rdman->ob_factory.subject_alloc = ob_subject_alloc;
@@ -283,7 +332,7 @@
 	goto err;
 
     addrm_ob = subject_add_observer(rdman->addrm_monitor,
-				    addrm_monitor_hdlr, NULL);
+				    addrm_monitor_hdlr, rdman);
     if(addrm_ob == NULL)
 	goto err;
 
@@ -299,7 +348,8 @@
 						 rdman->root_coord,
 						 OBJT_COORD);
     rdman->root_coord->flags |= COF_OWN_CANVAS;
-    rdman->root_coord->canvas = cr;
+    rdman->root_coord->canvas_info =
+	coord_canvas_info_new(rdman, rdman->root_coord, cr);
     rdman->root_coord->opacity = 1;
 
     rdman->cr = cr;
@@ -334,6 +384,12 @@
 	elmpool_free(rdman->paint_color_pool);
     if(rdman->pent_pool)
 	elmpool_free(rdman->pent_pool);
+    if(rdman->coord_canvas_pool)
+	elmpool_free(rdman->coord_canvas_pool);
+    DARRAY_DESTROY(&rdman->dirty_coords);
+    DARRAY_DESTROY(&rdman->dirty_geos);
+    DARRAY_DESTROY(&rdman->gen_geos);
+    DARRAY_DESTROY(&rdman->zeroing_coords);
     return ERR;
 }
 
@@ -375,7 +431,9 @@
     }
     if(saved_shape != NULL)
 	rdman_shape_free(rdman, saved_shape);
-
+    
+    coord_canvas_info_free(rdman, rdman->root_coord->canvas_info);
+    
     elmpool_free(rdman->coord_pool);
     elmpool_free(rdman->geo_pool);
     elmpool_free(rdman->shnode_pool);
@@ -383,11 +441,12 @@
     elmpool_free(rdman->subject_pool);
     elmpool_free(rdman->paint_color_pool);
     elmpool_free(rdman->pent_pool);
+    elmpool_free(rdman->coord_canvas_pool);
 
     DARRAY_DESTROY(&rdman->dirty_coords);
     DARRAY_DESTROY(&rdman->dirty_geos);
-    DARRAY_DESTROY(&rdman->dirty_areas);
     DARRAY_DESTROY(&rdman->gen_geos);
+    DARRAY_DESTROY(&rdman->zeroing_coords);
 }
 
 
@@ -558,7 +617,7 @@
     /*! \note default opacity == 1 */
     coord->opacity = 1;
     if(parent)
-	coord->canvas = parent->canvas;
+	coord->canvas_info = parent->canvas_info;
     rdman->n_coords++;
 
     coord->order = ++rdman->next_coord_order;
@@ -636,9 +695,11 @@
     if(cm_cnt || rdman_is_dirty(rdman))
 	return rdman_coord_free_postponse(rdman, coord);
 
-    /* Free canvas (\ref redraw) */
-    if(coord->flags & COF_OWN_CANVAS)
-	free_canvas(coord->canvas);
+    /* Free canvas and canvas_info (\ref redraw) */
+    if(coord->flags & COF_OWN_CANVAS) {
+	canvas_free(_coord_get_canvas(coord));
+	coord_canvas_info_free(rdman, coord->canvas_info);
+    }
 
     RM_CHILD(parent, coord);
     subject_free(coord->mouse_event);
@@ -732,6 +793,11 @@
 	}
 
 	add_dirty_coord(rdman, child);
+
+	if(child->flags & COF_OWN_CANVAS) {
+	    preorder_coord_skip_subtree(child);
+	    continue;
+	}
     }
 
     return OK;
@@ -813,45 +879,46 @@
 	sh_show(shape);
 }
 
-/*! \brief Setup canvas for the coord.
+/*! \brief Setup canvas_info for the coord.
  *
  * Own a canvas or inherit it from parent.
  * \sa
  * - \ref redraw
  */
-static void setup_canvas(redraw_man_t *rdman, coord_t *coord) {
+static void setup_canvas_info(redraw_man_t *rdman, coord_t *coord) {
     if(coord->parent == NULL)
 	return;
 
-    if(coord->opacity != 1) {
+    if(coord->opacity != 1 || coord_is_cached(coord)) {
 	if(!(coord->flags & COF_OWN_CANVAS)) {
-	    coord->canvas = new_canvas(rdman);
+	    /* canvas is assigned latter, in zeroing_coord() */
+	    coord->canvas_info = coord_canvas_info_new(rdman, coord, NULL);
 	    coord->flags |= COF_OWN_CANVAS;
 	}
     } else {
 	if(coord->flags & COF_OWN_CANVAS) {
-	    free_canvas(coord->canvas);
+	    canvas_free(_coord_get_canvas(coord));
+	    coord_canvas_info_free(rdman, coord->canvas_info);
 	    coord->flags &= ~COF_OWN_CANVAS;
 	}
-	coord->canvas = coord->parent->canvas;
+	/* This must here to keep coords that do not own canvas
+	 * can always point to right canvas_info.  Since, they
+	 * don't know when will parent change it's canvas_info.
+	 */
+	coord->canvas_info = coord->parent->canvas_info;
     }
 }
 
-static int clean_coord(redraw_man_t *rdman, coord_t *coord) {
+static int coord_clean_members_n_compute_area(coord_t *coord) {
     geo_t *geo;
     /*! \note poses is shared by invokings, it is not support reentrying. */
     static co_aix (*poses)[2];
     static int max_poses = 0;
     int cnt, pos_cnt;
-
-    setup_canvas(rdman, coord);
-
-    compute_aggr_of_coord(coord);
-
+    
     /* Clean member shapes. */
     cnt = 0;
     FORMEMBERS(coord, geo) {
-	SWAP(geo->cur_area, geo->last_area, area_t *);
 	clean_shape(geo->shape);
 	cnt++;
     }
@@ -871,9 +938,153 @@
 	pos_cnt += 2;
     }
 
-    SWAP(coord->cur_area, coord->last_area, area_t *);
     area_init(coord->cur_area, pos_cnt, poses);
     
+    return OK;
+}
+
+/*! \brief Shift space of coord to align left-top of minimum covering.
+ *
+ * Align left-top of minimum rectangle covering occupied area of
+ * sub-graphic to origin of the space.
+ */
+static
+void zeroing_coord(redraw_man_t *rdman, coord_t *coord) {
+    coord_t *cur;
+    area_t *area;
+    co_aix min_x, min_y;
+    co_aix max_x, max_y;
+    co_aix x, y;
+    int w, h;
+    int c_w, c_h;
+    cairo_t *canvas;
+    co_aix *aggr;
+    co_aix poses[2][2];
+
+    if(coord->parent == NULL)	/*! \note Should not zeroing root coord */
+	abort();
+    if(!(coord_is_zeroing(coord)))
+	abort();
+
+    coord_clear_zeroing(coord);
+
+    /*
+     * Compute minimum overing area of sub-graphic
+     */
+    area = coord_get_area(coord);
+    min_x = area->x;
+    min_y = area->y;
+    max_x = min_x + area->w;
+    max_y = min_y + area->h;
+
+    FOR_COORDS_PREORDER(coord, cur) {
+	area = coord_get_area(cur);
+	if(area->x < min_x)
+	    min_x = area->x;
+	if(area->y < min_y)
+	    min_y = area->y;
+	
+	x = area->x + area->w;
+	y = area->y + area->h;
+	
+	if(x > max_x)
+	    max_x = x;
+	if(y > max_y)
+	    max_y = y;
+	if(cur->flags & COF_OWN_CANVAS)
+	    preorder_coord_skip_subtree(cur);
+    }
+
+    w = max_x - min_x;
+    h = max_y - min_y;
+    
+    /*
+     * Setup area of the coord
+     */
+    aggr = coord_get_aggr_matrix(coord);
+    x = y = 0;
+    coord_trans_pos(coord->parent, &x, &y);
+    poses[0][0] = x;
+    poses[0][1] = y;
+    x = w;
+    y = h;
+    coord_trans_pos(coord->parent, &x, &y);
+    poses[1][0] = x;
+    poses[1][1] = y;
+
+    area_init(coord_get_area(coord), 2, poses);
+    
+    canvas = _coord_get_canvas(coord);
+    if(canvas)
+	canvas_get_size(canvas, &c_w, &c_h);
+    else
+	c_w = c_h = 0;
+    
+    if(!coord_get_flags(coord, COF_JUST_CLEAN) &&
+       min_x >= 0 && min_y >= 0 && max_x <= c_w && max_y <= c_h)
+	/* Canvas fully cover sub-graphic. */
+	return;
+    
+    /*
+     * Adjust matrics of descendants to align left-top corner of
+     * minimum covering area with origin of space defined by
+     * zeroing coord.
+     */
+    FOR_COORDS_PREORDER(coord, cur) {
+	aggr = coord_get_aggr_matrix(cur);
+	aggr[3] -= min_x;
+	aggr[5] -= min_y;
+	if(coord_get_flags(cur, COF_OWN_CANVAS)) {
+	    /*
+	     * Coords, zeroing, is zeroed in preorder of tree.
+	     * So, they are zeroed after ancesters with correctly
+	     * coord_t::aggr_matrix of parent coord to zeroing.
+	     */
+	    preorder_coord_skip_subtree(cur);
+	    area = coord_get_area(cur);
+	    area->x -= min_x;
+	    area->y -= min_y;
+	} else
+	    coord_clean_members_n_compute_area(cur);
+    }
+    
+    /*
+     * Setup canvas
+     */
+    if(canvas == NULL || w > c_w || h > c_w) {
+	if(canvas)
+	    canvas_free(canvas);
+	canvas = canvas_new(w, h);
+	_coord_set_canvas(coord, canvas);
+    }
+
+    area = &coord->canvas_info->cached_dirty_area;
+    area->x = 0;
+    area->y = 0;
+    area->w = w;
+    area->h = h;
+    DARRAY_CLEAN(_coord_get_dirty_areas(coord));
+    add_dirty_area(rdman, coord, area);
+}
+
+/*! \brief Clean dirty coords.
+ *
+ * \note coords their opacity != 1 are also traded as cached ones.
+ */
+static int clean_coord(redraw_man_t *rdman, coord_t *coord) {
+    int r;
+    
+    setup_canvas_info(rdman, coord);
+
+    if(coord->flags & COF_OWN_CANVAS)
+	compute_aggr_of_cached_coord(coord);
+    else
+	compute_aggr_of_coord(coord);
+
+    r = coord_clean_members_n_compute_area(coord);
+    if(r != OK)
+	return ERR;
+
     coord->flags &= ~COF_DIRTY;
 
     return OK;
@@ -900,10 +1111,9 @@
 	    if(r != OK)
 		return ERR;
 	    /* These two steps can be avoided for drawing all. */
-	    add_dirty_area(rdman, &coord->areas[0]);
-	    add_dirty_area(rdman, &coord->areas[1]);
+	    add_dirty_area(rdman, coord, &coord->areas[0]);
+	    add_dirty_area(rdman, coord, &coord->areas[1]);
 	}
-	rdman->dirty_coords.num = 0;
     }
     return OK;
 }
@@ -913,6 +1123,7 @@
     int n_dirty_geos;
     geo_t **dirty_geos;
     geo_t *visit_geo;
+    coord_t *coord;
 
     n_dirty_geos = rdman->dirty_geos.num;
     if(n_dirty_geos > 0) {
@@ -922,28 +1133,303 @@
 	    if(!(visit_geo->flags & GEF_DIRTY))
 		continue;
 
-	    SWAP(visit_geo->cur_area, visit_geo->last_area, area_t *);
 	    clean_shape(visit_geo->shape);
-	    add_dirty_area(rdman, visit_geo->cur_area);
-	    add_dirty_area(rdman, visit_geo->last_area);
+	    coord = geo_get_coord(visit_geo);
+	    add_dirty_area(rdman, coord, visit_geo->cur_area);
+	    add_dirty_area(rdman, coord, visit_geo->last_area);
+	}
+    }    
+
+    return OK;
+}
+
+/*! \brief Add canvas owner of dirty geos to coord_t::zeroing_coords.
+ *
+ * All possible coords that need a zeroing have at least one dirty geo.
+ */
+static int add_rdman_zeroing_coords(redraw_man_t *rdman) {
+    int i;
+    int n_dirty_geos;
+    geo_t **dirty_geos, *geo;
+    int n_dirty_coords;
+    coord_t **dirty_coords, *coord;
+
+    n_dirty_geos = rdman->dirty_geos.num;
+    dirty_geos = rdman->dirty_geos.ds;
+    for(i = 0; i < n_dirty_geos; i++) {
+	geo = dirty_geos[i];
+	coord = geo_get_coord(geo)->canvas_info->owner;
+	while(!coord_get_flags(coord, COF_MUST_ZEROING | COF_TEMP_MARK)) {
+	    coord_set_flags(coord, COF_TEMP_MARK);
+	    if(coord_is_root(coord))
+		break;
+	    coord = coord->parent->canvas_info->owner;
+	}
+    }
+    
+    n_dirty_coords = rdman->dirty_coords.num;
+    dirty_coords = rdman->dirty_coords.ds;
+    for(i = 0; i < n_dirty_coords; i++) {
+	coord = dirty_coords[i]->canvas_info->owner;
+	while(!coord_get_flags(coord, COF_MUST_ZEROING | COF_TEMP_MARK)) {
+	    coord_set_flags(coord, COF_TEMP_MARK);
+	    if(coord_is_root(coord))
+		break;
+	    coord = coord->parent->canvas_info->owner;
+	}
+    }
+    
+    FOR_COORDS_PREORDER(rdman->root_coord, coord) {
+	if(!coord_get_flags(coord, COF_TEMP_MARK)) {
+	    preorder_coord_skip_subtree(coord);
+	    continue;
 	}
-	rdman->dirty_geos.num = 0;
-    }    
+	add_zeroing_coord(rdman, coord);
+	coord_clear_flags(coord, COF_TEMP_MARK);
+    }
+    
+    return OK;
+}
+
+/*! \brief Zeroing coords in redraw_man_t::zeroing_coords.
+ *
+ * \note redraw_man_t::zeroing_coords must in ascent partial order of tree.
+ */
+static int zeroing_rdman_coords(redraw_man_t *rdman) {
+    int i;
+    coords_t *all_zeroing;
+    coord_t *coord;
+   
+    all_zeroing = &rdman->zeroing_coords;
+    for(i = all_zeroing->num - 1; i >= 0; i--) {
+	coord = all_zeroing->ds[i];
+	if(coord_is_root(coord))
+	    continue;
+	zeroing_coord(rdman, coord);
+    }
+
+    return OK;
+}
+
+/* \brief Compute matrix from cached canvas to parent device space.
+ */
+static void compute_cached_2_pdev_matrix(coord_t *coord,
+					   co_aix canvas2pdev_matrix[6]) {
+    coord_t *parent;
+    co_aix *aggr;
+    co_aix *matrix, *paggr;
+    co_aix scale_x, scale_y;
+    co_aix shift_x, shift_y;
+    co_aix canvas2p[6];
+
+    aggr = coord_get_aggr_matrix(coord);
+    matrix = coord->matrix;
+    parent = coord->parent;
+    paggr = coord_get_aggr_matrix(parent);
+    
+    scale_x = matrix[0] / aggr[0];
+    scale_y = matrix[3] / aggr[3];
+    shift_x = matrix[2] - scale_x * aggr[2];
+    shift_y = matrix[5] - scale_y * aggr[5];
+
+    canvas2p[0] = scale_x;
+    canvas2p[1] = 0;
+    canvas2p[2] = shift_x;
+    canvas2p[3] = 0;
+    canvas2p[4] = scale_y;
+    canvas2p[5] = shift_y;
+
+    matrix_mul(paggr, canvas2p, canvas2pdev_matrix);
+}
+
+/*! \brief Add aggregated dirty areas to ancestor.
+ *
+ * Dirty areas are aggregated into two areas.  It assumes that even or odd
+ * ones are old areas or new areas repsective.  So, all even ones are
+ * aggregated in an area, and odd ones are in another.
+ */
+static void add_aggr_dirty_areas_to_ancestor(redraw_man_t *rdman,
+					      coord_t *coord) {
+    int i;
+    int n_areas;
+    co_aix poses0[2][2], poses1[2][2];
+    co_aix reverse[6];
+    co_aix canvas2pdev_matrix[6];
+    area_t **areas, *area;
+    area_t *area0, *area1;
+    coord_t *parent, *pcached_coord;
+
+    n_areas = _coord_get_dirty_areas(coord)->num;
+    areas = _coord_get_dirty_areas(coord)->ds;
+    if(n_areas == 0)
+	abort();		/* should not happen! */
+    
+    area0 = coord->canvas_info->aggr_dirty_areas;
+    area1 = area0 + 1;
+
+    for(i = 0; i < n_areas; i++) {
+	area = areas[i];
+	if(area->w != 0 || area->h != 0)
+	    break;
+    }
+
+    if(i < n_areas) {
+	area = areas[i++];
+	poses0[0][0] = area->x;
+	poses0[0][1] = area->y;
+	poses0[1][0] = area->x + area->w;
+	poses0[1][1] = area->y + area->h;
+    } else {
+	poses0[0][0] = 0;
+	poses0[0][1] = 0;
+	poses0[1][0] = 0;
+	poses0[1][1] = 0;
+    }
+    
+    if(i < n_areas) {
+	area = areas[i++];
+	poses1[0][0] = area->x;
+	poses1[0][1] = area->y;
+	poses1[1][0] = area->x + area->w;
+	poses1[1][1] = area->y + area->h;
+    } else {
+	poses1[0][0] = 0;
+	poses1[0][1] = 0;
+	poses1[1][0] = 0;
+	poses1[1][1] = 0;
+    }
+	
+    for(; i < n_areas - 1;) {
+	/* Even areas */
+	area = areas[i++];
+	if(area->w != 0 || area->h != 0) {
+	    poses0[0][0] = MIN(poses0[0][0], area->x);
+	    poses0[0][1] = MIN(poses0[0][1], area->y);
+	    poses0[1][0] = MAX(poses0[1][0], area->x + area->w);
+	    poses0[1][1] = MAX(poses0[1][1], area->y + area->h);
+	}
+	/* Odd areas */
+	area = areas[i++];
+	if(area->w != 0 || area->h != 0) {
+	    poses1[0][0] = MIN(poses1[0][0], area->x);
+	    poses1[0][1] = MIN(poses1[0][1], area->y);
+	    poses1[1][0] = MAX(poses1[1][0], area->x + area->w);
+	    poses1[1][1] = MAX(poses1[1][1], area->y + area->h);
+	}
+    }
+
+    if(i < n_areas) {
+	area = areas[i];
+	if(area->w != 0 || area->h != 0) {
+	    poses0[0][0] = MIN(poses0[0][0], area->x);
+	    poses0[0][1] = MIN(poses0[0][1], area->y);
+	    poses0[1][0] = MAX(poses0[1][0], area->x + area->w);
+	    poses0[1][1] = MAX(poses0[1][1], area->y + area->h);
+	}
+    }
+
+    parent = coord->parent;
+    pcached_coord = parent->canvas_info->owner;
+    
+    compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
+    
+    matrix_trans_pos(canvas2pdev_matrix, poses0[0], poses0[0] + 1);
+    matrix_trans_pos(canvas2pdev_matrix, poses0[1], poses0[1] + 1);
+    area_init(area0, 2, poses0);
+    add_dirty_area(rdman, pcached_coord, area0);
+
+    matrix_trans_pos(canvas2pdev_matrix, poses1[0], poses1[0] + 1);
+    matrix_trans_pos(canvas2pdev_matrix, poses1[1], poses1[1] + 1);
+    area_init(area1, 2, poses1);
+    if(area1->w != 0 || area1->h != 0)
+	add_dirty_area(rdman, pcached_coord, area1);
+
+    if(coord_get_flags(coord, COF_JUST_CLEAN) &&
+       !coord_get_flags(pcached_coord, COF_JUST_CLEAN))
+	add_dirty_area(rdman, pcached_coord, coord->last_area);
+}
+
+static int add_rdman_aggr_dirty_areas(redraw_man_t *rdman) {
+    int i;
+    int n_zeroing;
+    coord_t **zeroings;
+    coord_t *coord;
+    
+    n_zeroing = rdman->zeroing_coords.num;
+    zeroings = rdman->zeroing_coords.ds;
+    for(i = n_zeroing - 1; i >= 0; i--) {
+	coord = zeroings[i];
+	if(!coord_is_root(coord))
+	    add_aggr_dirty_areas_to_ancestor(rdman, coord);
+    }
+
+    return OK;
+}
+
+static int add_rdman_cached_dirty_areas(redraw_man_t *rdman) {
+    int i;
+    coord_t *coord, **dirty_coords;
+    int n_dirty_coords;
+
+    n_dirty_coords = rdman->dirty_coords.num;
+    dirty_coords = rdman->dirty_coords.ds;
+    for(i = 0; i < n_dirty_coords; i++) {
+	coord = dirty_coords[i];
+	if(coord_get_flags(coord, COF_OWN_CANVAS)) {
+	    add_dirty_area(rdman, coord, coord->cur_area);
+	    add_dirty_area(rdman, coord, coord->last_area);
+	}
+    }
 
     return OK;
 }
 
 static int clean_rdman_dirties(redraw_man_t *rdman) {
     int r;
+    int i;
+    coord_t **coords;
+    geo_t **geos;
+
+    coords = rdman->dirty_coords.ds;
+    for(i = 0; i < rdman->dirty_coords.num; i++)
+	if(coords[i]->flags & COF_DIRTY)
+	    SWAP(coords[i]->cur_area, coords[i]->last_area, area_t *);
+
+    geos = rdman->dirty_geos.ds;
+    for(i = 0; i < rdman->dirty_geos.num; i++)
+	if(geos[i]->flags & GEF_DIRTY)
+	    SWAP(geos[i]->cur_area, geos[i]->last_area, area_t *);
 
     r = clean_rdman_coords(rdman);
     if(r != OK)
 	return ERR;
+    
+    for(i = 0; i < rdman->dirty_coords.num; i++)
+	coord_set_flags(rdman->dirty_coords.ds[i], COF_JUST_CLEAN);
 
     r = clean_rdman_geos(rdman);
     if(r != OK)
 	return ERR;
 
+    r = add_rdman_zeroing_coords(rdman);
+    if(r != OK)
+	return ERR;
+
+    r = zeroing_rdman_coords(rdman);
+    if(r != OK)
+	return ERR;
+
+    r = add_rdman_aggr_dirty_areas(rdman);
+    if(r != OK)
+	return ERR;
+
+    r = add_rdman_cached_dirty_areas(rdman);
+    if(r != OK)
+	return ERR;
+
+    for(i = 0; i < rdman->dirty_coords.num; i++)
+	coord_clear_flags(rdman->dirty_coords.ds[i], COF_JUST_CLEAN);
+    
     return OK;
 }
 
@@ -1028,22 +1514,37 @@
 }
 
 #ifndef UNITTEST
+static void clear_canvas(canvas_t *canvas) {
+    cairo_operator_t old_op;
+
+    old_op = cairo_get_operator(canvas);
+    cairo_set_operator(canvas, CAIRO_OPERATOR_CLEAR);
+    cairo_paint(canvas);
+    cairo_set_operator(canvas, old_op);
+}
+
 static void clean_canvas(cairo_t *cr, co_aix w, co_aix h) {
+    cairo_operator_t saved_op;
+    
+    saved_op = cairo_get_operator(cr);
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+    
     /*! \todo clean to background color. */
-    cairo_set_source_rgb(cr, 1, 1, 1);
-#if 1
+    cairo_set_source_rgba(cr, 1, 1, 1, 1);
+    
     /* For some unknown reasons, cairo_paint() can not erease
      * painted graphic cleanly.  So, cairo_fill() are used to
      * replace it.
      */
     cairo_rectangle(cr, 0, 0, w, h);
     cairo_fill(cr);
-#else
-    cairo_paint(cr);
-#endif
+
+    cairo_set_operator(cr, saved_op);
 }
 
 static void clean_canvas_black(cairo_t *cr, co_aix w, co_aix h) {
+    clear_canvas(cr);
+    
     /*! \todo clean to background color. */
     cairo_set_source_rgba(cr, 0, 0, 0, 0);
     cairo_paint(cr);
@@ -1054,6 +1555,7 @@
     int i;
     area_t *area;
 
+    cairo_new_path(cr);
     for(i = 0; i < n_dirty_areas; i++) {
 	area = dirty_areas[i];
 	cairo_rectangle(cr, area->x, area->y, area->w, area->h);
@@ -1061,16 +1563,21 @@
     cairo_clip(cr);
 }
 
-static void reset_clip(redraw_man_t *rdman) {
-    cairo_reset_clip(rdman->backend);
+static void reset_clip(canvas_t *cr) {
+    cairo_reset_clip(cr);
 }
 
 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
 			      area_t **dirty_areas) {
+    cairo_operator_t saved_op;
+    
     if(n_dirty_areas)
 	make_clip(rdman->backend, n_dirty_areas, dirty_areas);
     
+    saved_op = cairo_get_operator(rdman->backend);
+    cairo_set_operator(rdman->backend, CAIRO_OPERATOR_SOURCE);
     cairo_paint(rdman->backend);
+    cairo_set_operator(rdman->backend, saved_op);
 }
 #else /* UNITTEST */
 static void clean_canvas(cairo_t *cr, co_aix w, co_aix h) {
@@ -1079,7 +1586,7 @@
 static void clean_canvas_black(cairo_t *cr, co_aix w, co_aix h) {
 }
 
-static void reset_clip(redraw_man_t *rdman) {
+static void reset_clip(canvas_t *cr) {
 }
 
 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
@@ -1087,54 +1594,88 @@
 }
 #endif /* UNITTEST */
 
-static int is_geo_in_areas(geo_t *geo,
+static int is_area_in_areas(area_t *area,
 			     int n_areas,
 			     area_t **areas) {
     int i;
 
     for(i = 0; i < n_areas; i++) {
-	if(areas_are_overlay(geo->cur_area, areas[i]))
+	if(areas_are_overlay(area, areas[i]))
 	    return 1;
     }
     return 0;
 }
 
-static void update_canvas_2_parent(redraw_man_t *rdman, coord_t *coord) {
+static int is_geo_in_areas(geo_t *geo,
+			     int n_areas,
+			     area_t **areas) {
+    return is_area_in_areas(geo->cur_area, n_areas, areas);
+}
+
+static void update_cached_canvas_2_parent(redraw_man_t *rdman,
+					  coord_t *coord) {
     cairo_t *pcanvas, *canvas;
     cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t cr_matrix;
+    co_aix reverse[6];
+    co_aix canvas2pdev_matrix[6];
 
-    if(coord == rdman->root_coord)
+    if(coord_is_root(coord))
 	return;
 
-    canvas = coord->canvas;
-    pcanvas = coord->parent->canvas;
+    compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
+    compute_reverse(canvas2pdev_matrix, reverse);
+    
+    cr_matrix.xx = reverse[0];
+    cr_matrix.xy = reverse[1];
+    cr_matrix.x0 = reverse[2];
+    cr_matrix.yx = reverse[3];
+    cr_matrix.yy = reverse[4];
+    cr_matrix.y0 = reverse[5];
+
+    canvas = _coord_get_canvas(coord);
+    pcanvas = _coord_get_canvas(coord->parent);
     surface = cairo_get_target(canvas);
-    cairo_set_source_surface(pcanvas, surface, 0, 0);
+    pattern = cairo_pattern_create_for_surface(surface);
+    cairo_pattern_set_matrix(pattern, &cr_matrix);
+    cairo_set_source(pcanvas, pattern);
     cairo_paint_with_alpha(pcanvas, coord->opacity);
 }
 
-static int draw_coord_shapes_in_areas(redraw_man_t *rdman,
-				       coord_t *coord,
-				       int n_areas,
-				       area_t **areas) {
+static int draw_coord_shapes_in_dirty_areas(redraw_man_t *rdman,
+					    coord_t *coord) {
     int dirty = 0;
     int r;
+    area_t **areas;
+    int n_areas;
+    cairo_t *canvas;
     geo_t *member;
     coord_t *child;
-    cairo_t *canvas;
     int mem_idx;
 
     if(coord->flags & COF_HIDDEN)
 	return OK;
-
-    canvas = coord->canvas;
+    
+    areas = _coord_get_dirty_areas(coord)->ds;
+    n_areas = _coord_get_dirty_areas(coord)->num;
+    canvas = _coord_get_canvas(coord);
+    
     member = FIRST_MEMBER(coord);
     mem_idx = 0;
     child = FIRST_CHILD(coord);
     while(child != NULL || member != NULL) {
 	if(child && child->before_pmem == mem_idx) {
-	    r = draw_coord_shapes_in_areas(rdman, child, n_areas, areas);
-	    dirty |= r;
+	    if(child->flags & COF_OWN_CANVAS) {
+		if(!(child->flags & COF_HIDDEN) &&
+		   is_area_in_areas(coord_get_area(child), n_areas, areas)) {
+		    update_cached_canvas_2_parent(rdman, child);
+		    dirty = 1;
+		}
+	    } else {
+		r = draw_coord_shapes_in_dirty_areas(rdman, child);
+		dirty |= r;
+	    }
 	    child = NEXT_CHILD(child);
 	} else {
 	    ASSERT(member != NULL);
@@ -1149,18 +1690,45 @@
 	}
     }
 
-    if(dirty && coord->flags & COF_OWN_CANVAS) {
-	update_canvas_2_parent(rdman, coord);
-	clean_canvas_black(coord->canvas, rdman->w, rdman->h);
-    } 
-
     return dirty;
 }
 
-static void draw_shapes_in_areas(redraw_man_t *rdman,
-				 int n_areas,
-				 area_t **areas) {
-    draw_coord_shapes_in_areas(rdman, rdman->root_coord, n_areas, areas);
+static int draw_dirty_cached_coord(redraw_man_t *rdman,
+				   coord_t *coord) {
+    area_t **areas, *area;
+    int n_areas;
+    cairo_t *canvas;
+    int i;
+    int r;
+    
+    areas = _coord_get_dirty_areas(coord)->ds;
+    n_areas = _coord_get_dirty_areas(coord)->num;
+    
+    for(i = 0; i < n_areas; i++) {
+	area = areas[i];
+	area->x = floorf(area->x);
+	area->y = floorf(area->y);
+	area->w = ceilf(area->w);
+	area->h = ceilf(area->h);
+    }
+
+    canvas = _coord_get_canvas(coord);
+    make_clip(canvas, n_areas, areas);
+    clear_canvas(canvas);
+
+    r = draw_coord_shapes_in_dirty_areas(rdman, coord);
+    
+    reset_clip(canvas);
+}
+
+static void draw_shapes_in_dirty_areas(redraw_man_t *rdman) {
+    int i;
+    coord_t *coord;
+
+    for(i = rdman->zeroing_coords.num - 1; i >= 0; i--) {
+	coord = rdman->zeroing_coords.ds[i];
+	draw_dirty_cached_coord(rdman, coord);
+    }
 }
 
 
@@ -1198,29 +1766,43 @@
  */
 int rdman_redraw_changed(redraw_man_t *rdman) {
     int r;
-    int n_dirty_areas;
-    area_t **dirty_areas;
     event_t event;
     subject_t *redraw;
-
+    int i;
+    coord_t *coord, **coords;
+    int n_areas;
+    area_t **areas;
+    
     r = clean_rdman_dirties(rdman);
     if(r != OK)
 	return ERR;
 
-    n_dirty_areas = rdman->dirty_areas.num;
-    dirty_areas = rdman->dirty_areas.ds;
-    if(n_dirty_areas > 0) {
+    if(rdman->n_dirty_areas > 0) {
 	/*! \brief Draw shapes in preorder of coord tree and support opacity
 	 * rules.
 	 */
-	clean_canvas(rdman->cr, rdman->w, rdman->h);
-	draw_shapes_in_areas(rdman, n_dirty_areas, dirty_areas);
-	copy_cr_2_backend(rdman, rdman->dirty_areas.num,
-			  rdman->dirty_areas.ds);
-	reset_clip(rdman);
+	draw_shapes_in_dirty_areas(rdman);
+	n_areas = _coord_get_dirty_areas(rdman->root_coord)->num;
+	areas = _coord_get_dirty_areas(rdman->root_coord)->ds;
+	copy_cr_2_backend(rdman, n_areas, areas);
+	reset_clip(rdman->backend);
+	for(i = 0; i < rdman->zeroing_coords.num; i++) {
+	    coord = rdman->zeroing_coords.ds[i];
+	    DARRAY_CLEAN(_coord_get_dirty_areas(coord));
+	}
+	rdman->n_dirty_areas = 0;
     }
-    rdman->dirty_areas.num = 0;
 
+    coords = rdman->zeroing_coords.ds;
+    for(i = 0; i < rdman->zeroing_coords.num; i++) {
+	coord = coords[i];
+	coord_clear_flags(coord, COF_MUST_ZEROING);
+    }
+
+    DARRAY_CLEAN(&rdman->dirty_coords);
+    DARRAY_CLEAN(&rdman->dirty_geos);
+    DARRAY_CLEAN(&rdman->zeroing_coords);
+    
     /* Free postponsed removing */
     free_free_objs(rdman);
 
@@ -1256,6 +1838,212 @@
  * - rdman_redraw_all()
  * - rdman_redraw_changed()
  * - draw_shapes_in_areas()
+ *
+ * \section img_cache Image Cache
+ * It costs time to redraw every component in a complete graphic.
+ * Image cache try to cache result of prviously rendering, and reusing it
+ * to avoid wasting CPU time on repeatitive and redundant rendering.
+ *
+ * \ref COF_FAST_CACHE and \ref COF_PRECISE_CACHE are used to tag a
+ * coord that it's
+ * rendering result is cached in fast way or precise way.  With fast cache,
+ * MB renders descendants of a coord in once, and reuse the result until it
+ * being dirty.  With precise cache, it alike fast cache, but it also
+ * performs rendering when an ancester of the coord transform it to larger
+ * size, in width or height.
+ *
+ * coord_t::aggr_matrix of a cached coord is computed from aggr_matrix of
+ * parent.  But, it does not use one from parent directly.  parent one is
+ * transformed as
+ * \code
+ * cache_scale_x = sqrt(p_matrix[0]**2 + p_matrix[3]**2);
+ * cache_scale_y = sqrt(p_matrix[1]**2 + p_matrix[4]**2);
+ * cache_p_matrix[0] = cache_scale_x;
+ * cache_p_matrix[1] = 0;
+ * cache_p_matrix[2] = range_shift_x;
+ * cache_p_matrix[3] = 0;
+ * cache_p_matrix[4] = cache_scale_y;
+ * cache_p_matrix[5] = range_shift_y;
+ * \endcode
+ * where p_matrix is parent one, and cache_p_matrix is one derived from
+ * parent one.  coord_t::aggr_matrix of a cached coord is
+ * \code
+ * aggr_matrix = cache_p_matrix * matrix
+ * \endcode
+ * where matrix is the transform being installed on the cached coord.
+ * range_shift_x and range_shift_y are defined above.
+ *
+ * cache_p_matrix rescales sub-graphic to an appropriately size
+ * (cache_scale_x, cache_scale_y) and aligns left-top of the minimum
+ * rectangle (range_shift_x, range_shift_y) that cover the area occupied
+ * by sub-graphic with origin of the space.
+ *
+ * The sub-graphic should be rendered on space defined by cache_p_matrix of
+ * cached one.  But rendering result are transformed to the space defined
+ * by parent with following matrix.
+ * \code
+ * draw_matrix = reverse(p_matrix * reverse(cache_p_matrix))
+ * \endcode
+ * With Cairo, draw_matrix is applied on source surface (canvas)
+ * to draw image to parent's surface (canvas).  draw_matrix is a function
+ * map points from parent space to the space of cached one.
+ *
+ * Cached coords are marked for changing transformation of ancestors only if
+ * following condition is true.
+ * \code
+ * cache_scale_x < sqrt(p_matrix[0]**2 + p_matrix[3]**2) ||
+ * cache_scale_y < sqrt(p_matrix[1]**2 + p_matrix[4]**2)
+ * \endcode
+ * where p_matrix is latest aggr_matrix of parent after changing
+ * transformation, and where cache_scale_* are ones mention above and computed
+ * before changing transformation of ancestors.
+ *
+ * Cache_scale_* can be recovered by following instructions.
+ * \code
+ * cache_scale_x = aggr_matrix[0] / matrix[0];
+ * cache_scale_y = aggr_matrix[4] / matrix[4];
+ * \endcode
+ *
+ * \section cache_area Area of cached coord
+ * - *_transform of shapes works as normal
+ *   - areas of descendants of cached coord are in space defined
+ *     by aggr_matrix of cached coord.
+ *   - descendants are marked with \ref COF_ANCESTOR_CACHE
+ * 
+ * Since *_transform of shapes compute area with aggr_matrix that is
+ * derived from aggr_matrix of a cached ancestor, area of
+ * \ref COF_ANCESTOR_CACHE ones should be transformed to device space in
+ * find_shape_at_pos() with following statement.
+ * \code
+ * area_matrix = p_matrix * reverse(cache_p_matrix)
+ * \endcode
+ * where cache_p_matrix and p_matrix are corresponding matrix of
+ * cached ancestor.  We can also perform transforming in reversed
+ * direction to transform point to space defined by aggr_matrix of cached
+ * coord.
+ *
+ * Since it is costly to transform area of \ref COF_ANCESTOR_CACHE ones to
+ * device space if more than one ancestor are cached, no ancestor of
+ * cached coord can be set to cached.
+ *
+ * \section cached_bounding Bounding box of cached coord and descendants
+ * Bounding box of a cached coord and it's descendants is the range that
+ * cached coord and descendants are rendered on canvas.  It is also called
+ * cached-bounding.
+ *
+ * range_shift_x and range_shift_y are computed by initailizing cache_p_matrix
+ * with range_shift_x == range_shift_y == 0 at first.  cache_p_matrix is
+ * used to compute aggr_matrix and cached-bounding in turn.  Then,
+ * range_shift_x and range_shift_y are initialized to negative of
+ * x-axis and y-axis, repectively, of left-top of cached-bounding.  Then,
+ * aggr_matrix of cached coord and descendants are updated by
+ * following statements.
+ * \code
+ * aggr_matrix[2] += range_shift_x;
+ * aggr_matrix[5] += range_shift_y;
+ * \endcode
+ * The statements shift the spaces to make cached-bounding
+ * aligned to origin of coordinate system.
+ * The purpose of range_shift_* is to reduce size of canvas used to cache
+ * rendering result.  The canvas are shrink to size the same as bounding
+ * box.
+ *
+ * \section cache_redraw How cache and redraw work together?
+ * When a coord and descedants are cached, the coord is flaged with
+ * COF_FAST_CACHE or COF_PRECISE_CACHE.  When a coord is marked dirty, all
+ * descendants are also marked dirty by rdman except descendants of cached
+ * ones.  But, cached ones are also marked dirty as normal ones.  The
+ * reason to mark cached ones is giving them a chance to update their
+ * area.
+ *
+ * For precise cached descendants, above rule has an exception.  They should
+ * also be marked dirty if cached coord should be rendered in a larger
+ * resize factor to get better output.
+ *
+ * coord_t::aggr_matrix and cached-bounding of cached coord must be computed
+ * in the way described in \ref cached_bounding.  Propagating range_shift_*
+ * to descendants must skip cached ones and their descendants.
+ * Range_shift_* are computed after updating descendants.  So, procedure
+ * of clean descendants of a cached one must performed in two phases.
+ * One for computing areas of descendants and one for propagating
+ * range_shift_*.
+ *
+ * A cached coord or/and descendants are dirty only for cached coord or
+ * descendants being marked dirty by application.  Once a cached coord or
+ * descendant is marked dirty, all descendants of marked one are also
+ * marked.  redraw_man_t::dirty_areas collects areas, in device space,
+ * that should be updated.  All shapes overlaid with any area in
+ * redraw_man_t::dirty_areas should be redraw.  Since descendants of cached
+ * coord compute their areas in spaces other than device space.
+ * Separated lists should be maintained for each cached coord and it's
+ * descendants.
+ *
+ * \section cache_imp Implementation of Cache
+ * Both cached coords and coords that opacity != 1 need a canvas to
+ * draw descendants on.  Both cases are traded in the same way.
+ * Every of them own a canvas_info to describe canvas and related
+ * information.  aggr_matrix of descendants must be adjusted to make
+ * left-top of range just at origin of canvas.  It can save space by setting
+ * just large enough to hold rendering result of descendants.  The process
+ * of adjusting is zeroing.
+ *
+ * Following is rules.
+ * - zeroing on a cached coord is performed by adjust coord_t::aggr_matrix 
+ *   of the cached coord and descendnats.
+ * - Clean coords works just like before without change.
+ *   - in preorder
+ * - never perform zeroing on root_coord.
+ * - zeroing on cached coords marked with \ref COF_MUST_ZEROING.
+ *   - when clean a descendant that moves out-side of it's canvas,
+ *     respective cached coord is marked with \ref COF_MUST_ZEROING.
+ *   - zeroing is performed immediately after clean coords.
+ *   - zeroing will not propagate acrossing boundary of cached coord.
+ *     - It will be stopped at descendants which are cached coords.
+ *     - coord_t::cur_area and coord_t::aggr_matrix of cached coords
+ *       must be ajdusted.
+ * - the area of a cached coord is defined in parent space.
+ *   - areas of descendants are defined in space defined by aggr_matrix of
+ *     cached coord.
+ *   - parent know the area in where cached coord and descendnats will
+ *     be draw.
+ * - cached coords keep their private dirty area list.
+ *   - private dirty areas of a cached coord are transformed and added to
+ *     parent cached coord.
+ *   - aggregates areas before adding to parent.
+ * - canvas of a cached coord is updated if
+ *   - descendants are dirty, or
+ *   - it-self is dirty.
+ * - change of a canvas must copy to canvas of parent space.
+ *   - a cached is updated if canvas of descendant cached coord is updated.
+ * - updating canvas is performed by redraw dirty areas.
+ *   - since dirty areas of cached ones would be aggregated and added to
+ *     parent, parent cached coord would copy it from cache of descedants.
+ * - descendant cached coords must be updated before ancestor cached coords.
+ *   - add dirty areas to parent immediately after updating canvas.
+ * - Making dirty coords is not propagated through cached ones.
+ *   - cached ones are also made dirty, but stop after that.
+ *
+ * Steps:
+ * - SWAP coord_t::cur_area of dirty coords.
+ * - SWAP geo_t::cur_area of dirty geos.
+ * - clean coords
+ *   - coord_t::aggr_matrix of cached coord is not the same as non-cached.
+ *   - see \ref img_cache
+ * - clean geos
+ * - Add canvas owner of dirty geos to redraw_man_t::zeroing_coords
+ *   - Cached ancestors of redraw_man_t::dirty_geos
+ *   - Cached ancestors of redraw_man_t::dirty_coords
+ *   - Cached ancestors of zeroed ones should also be zeroed.
+ * - zeroing
+ *   - Add more dirty areas if canvas should be fully redrawed.
+ *   - From leaf to root.
+ * - add aggregated dirty areas from descendant cached coords to ancestors.
+ *   - Must include old area of cached coords if it is just clean and
+ *     parent cached one is not just clean.
+ *   - Just clean is a coord cleaned in last time of cleaning coords.
+ * - draw dirty areas
+ *   - areas are rounded to N at first.
+ *   - from leaf to root.
  */
 
 int rdman_redraw_all(redraw_man_t *rdman) {
@@ -1274,7 +2062,7 @@
     area.w = 1024;
     area.h = 1024;
 #endif
-    add_dirty_area(rdman, &area);
+    add_dirty_area(rdman, rdman->root_coord, &area);
 
     r = rdman_redraw_changed(rdman);
     if(r != OK)
@@ -1292,13 +2080,15 @@
     area.y = y;
     area.w = w;
     area.h = h;
-    add_dirty_area(rdman, &area);
+    add_dirty_area(rdman, rdman->root_coord, &area);
 
     r = rdman_redraw_changed(rdman);
 
     return r;
 }
 
+/*! \brief Helping function to travel descendant shapes of a coord.
+ */
 geo_t *rdman_geos(redraw_man_t *rdman, geo_t *last) {
     geo_t *next;
     coord_t *coord;
--- a/tools/svg2code.py	Thu Mar 05 07:53:58 2009 +0800
+++ b/tools/svg2code.py	Thu Mar 05 07:54:35 2009 +0800
@@ -130,11 +130,14 @@
     except:
         style_str = node.getAttribute('style')
         prop_map = get_style_map(style_str)
-
-    try:
+        pass
+    
+    if node.hasAttribute('fill-opacity'):
+        opacity = float(node.getAttribute('fill-opacity'))
+    elif node.hasAttribute('opacity'):
         opacity = float(node.getAttribute('opacity'))
-    except:
-        opacity = 1.0
+    else:
+        opacity = 1
         pass
 
     try:
@@ -160,9 +163,11 @@
             raise ValueError, '\'%s\' is an invalid value for fill.' % (fill)
         pass
 
-    try:
+    if node.hasAttribute('stroke-opacity'):
         stroke_opacity = float(node.getAttribute('stroke-opacity'))
-    except:
+    elif node.hasAttribute('opacity'):
+        stroke_opacity = float(node.getAttribute('opacity'))
+    else:
         stroke_opacity = 1.0
         pass