diff src/redraw_man.c @ 314:6c350fc92ae3

Cache rednering result is now workable. - Know issues - For unknow issue, CAIRO_OPERATOR_CLEAR will not be limited by clipping. Image will be cleaned in a strange way. Maybe, it is a bug of Cairo.
author Thinker K.F. Li <thinker@branda.to>
date Thu, 05 Mar 2009 00:54:42 +0800
parents 13ce87b6dbf5
children d0f8642d3508
line wrap: on
line diff
--- a/src/redraw_man.c	Thu Mar 05 00:54:42 2009 +0800
+++ b/src/redraw_man.c	Thu Mar 05 00:54:42 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,42 @@
 }
 
 #ifndef UNITTEST
+static void clear_canvas(canvas_t *canvas) {
+    cairo_operator_t old_op;
+
+#if 1
+    old_op = cairo_get_operator(canvas);
+    cairo_set_operator(canvas, CAIRO_OPERATOR_CLEAR);
+    cairo_paint(canvas);
+    cairo_set_operator(canvas, old_op);
+#else
+    cairo_set_source_rgba(canvas, 0, 0, 0, 0);
+    cairo_paint(canvas);
+#endif
+}
+
 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);
@@ -1061,16 +1567,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 +1590,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 +1598,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 +1694,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 +1770,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 +1842,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 +2066,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 +2084,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;