changeset 138:9f4fc9ecfd1f

Make shapes and coords drawed in post-order of tree. 1. Add opacity for each coord. 2. Trival and draw tree of shapes and coords in post-order. 3. Coords have a before_pmem member variable to note it's order been draw. It is relative to it's siblings and member shapes of parent coord.
author Thinker K.F. Li <thinker@branda.to>
date Mon, 22 Sep 2008 11:45:00 +0800
parents 5dcfaafd1a9c
children 1695a4b02b14
files src/coord.c src/mb_types.h src/redraw_man.c
diffstat 3 files changed, 234 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/src/coord.c	Fri Sep 19 09:53:17 2008 +0800
+++ b/src/coord.c	Mon Sep 22 11:45:00 2008 +0800
@@ -123,7 +123,7 @@
 coord_t *preorder_coord_subtree(coord_t *root, coord_t *last) {
     coord_t *next;
 
-    ASSERT(last == NULL);
+    ASSERT(last != NULL);
     
     if(STAILQ_HEAD(last->children))
 	next = STAILQ_HEAD(last->children);
@@ -140,6 +140,32 @@
     return next;
 }
 
+coord_t *postorder_coord_subtree(coord_t *root, coord_t *last) {
+    coord_t *next;
+
+    if(root == last)
+	return NULL;
+    
+    if(last == NULL) {
+	/* Go most left leaf. */
+	next = root;
+	while(STAILQ_HEAD(next->children))
+	    next = STAILQ_HEAD(next->children);
+	return next;
+    }
+
+    next = last;
+    if(STAILQ_NEXT(coord_t, sibling, next) == NULL) /* most right */
+	return next->parent;
+
+    /* Go most left leaf of right sibling sub-tree. */
+    next = STAILQ_NEXT(coord_t, sibling, next);
+    while(STAILQ_HEAD(next->children))
+	next = STAILQ_HEAD(next->children);
+
+    return next;
+}
+
 void sh_attach_coord(shape_t *sh, coord_t *coord) {
     STAILQ_INS_TAIL(coord->members, shape_t, coord_mem_next, sh);
     sh->coord = coord;
--- a/src/mb_types.h	Fri Sep 19 09:53:17 2008 +0800
+++ b/src/mb_types.h	Mon Sep 22 11:45:00 2008 +0800
@@ -88,6 +88,14 @@
 typedef struct _coord {
     unsigned int order;
     unsigned int flags;
+    co_aix opacity;
+    /*! Own one or inherit from an ancestor.
+     * Setup it when clean coords.
+     * \sa
+     * - \ref COF_OWN_CANVAS
+     * - \ref redraw
+     */
+    cairo_t *canvas;
     area_t *cur_area, *last_area;
     area_t areas[2];
 
@@ -97,12 +105,17 @@
     struct _coord *parent;
     STAILQ(struct _coord) children;
     struct _coord *sibling;
+    unsigned int before_pmem;	/*!< \brief The coord is before nth member
+				 * of parent. */
 
     STAILQ(shape_t) members;	/*!< All shape_t objects in this coord. */
     subject_t *mouse_event;
 } coord_t;
 #define COF_DIRTY 0x1
 #define COF_HIDDEN 0x2
+#define COF_OWN_CANVAS 0x4	/*!< A coord owns a canvas or inherit it
+				 * from an ancestor.
+				 */
 
 extern void coord_init(coord_t *co, coord_t *parent);
 extern void coord_trans_pos(coord_t *co, co_aix *x, co_aix *y);
@@ -110,6 +123,7 @@
 extern void compute_aggr_of_coord(coord_t *coord);
 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);
 #define coord_hide(co) do { co->flags |= COF_HIDDEN; } while(0)
 #define coord_show(co) do { co->flags &= ~COF_HIDDEN; } while(0)
 #define coord_get_mouse_event(coord) ((coord)->mouse_event)
--- a/src/redraw_man.c	Fri Sep 19 09:53:17 2008 +0800
+++ b/src/redraw_man.c	Mon Sep 22 11:45:00 2008 +0800
@@ -112,6 +112,31 @@
     poses[1][1] = area->y + area->h;;
 }
 
+static cairo_t *new_canvas(redraw_man_t *rdman) {
+#ifndef UNITTEST
+    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);
+
+    return cr;
+#else
+    return NULL;
+#endif
+}
+
+static void free_canvas(cairo_t *canvas) {
+#ifndef UNITTEST
+    cairo_destroy(canvas);
+#endif
+}
+
 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr, cairo_t *backend) {
     extern void redraw_man_destroy(redraw_man_t *rdman);
 
@@ -165,6 +190,9 @@
     rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory,
 						 rdman->root_coord,
 						 OBJT_COORD);
+    rdman->root_coord->flags |= COF_OWN_CANVAS;
+    rdman->root_coord->canvas = cr;
+    rdman->root_coord->opacity = 1;
 
     rdman->cr = cr;
     rdman->backend = backend;
@@ -173,6 +201,15 @@
 }
 
 void redraw_man_destroy(redraw_man_t *rdman) {
+    coord_t *coord, *saved_coord;
+
+    coord = postorder_coord_subtree(rdman->root_coord, NULL);
+    while(coord) {
+	saved_coord = coord;
+	coord = postorder_coord_subtree(rdman->root_coord, coord);
+	rdman_coord_free(rdman, saved_coord);
+    }
+
     elmpool_free(rdman->coord_pool);
     elmpool_free(rdman->geo_pool);
     elmpool_free(rdman->shnode_pool);
@@ -321,6 +358,10 @@
     coord->mouse_event = subject_new(&rdman->ob_factory,
 				     coord,
 				     OBJT_COORD);
+    /*! \note default opacity == 1 */
+    coord->opacity = 1;
+    if(parent)
+	coord->canvas = parent->canvas;
     rdman->n_coords++;
 
     coord->order = ++rdman->next_coord_order;
@@ -356,6 +397,10 @@
     if(STAILQ_HEAD(coord->children) != NULL)
 	return ERR;
 
+    /* Free canvas (\ref redraw) */
+    if(coord->flags & COF_OWN_CANVAS)
+	free_canvas(coord->canvas);
+
     STAILQ_REMOVE(parent->children, coord_t, sibling, coord);
     subject_free(&rdman->ob_factory, coord->mouse_event);
     elmpool_elm_free(rdman->coord_pool, coord);
@@ -481,12 +526,38 @@
 	shape->geo->flags &= ~GEF_HIDDEN;
 }
 
-static int clean_coord(coord_t *coord) {
+/*! \brief Setup canvas 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) {
+    if(coord->parent == NULL)
+	return;
+
+    if(coord->opacity != 1) {
+	if(!(coord->flags & COF_OWN_CANVAS)) {
+	    coord->canvas = new_canvas(rdman);
+	    coord->flags |= COF_OWN_CANVAS;
+	}
+    } else {
+	if(coord->flags & COF_OWN_CANVAS) {
+	    free_canvas(coord->canvas);
+	    coord->flags &= ~COF_OWN_CANVAS;
+	}
+	coord->canvas = coord->parent->canvas;
+    }
+}
+
+static int clean_coord(redraw_man_t *rdman, coord_t *coord) {
     shape_t *shape;
     geo_t *geo;
     co_aix (*poses)[2];
     int cnt, pos_cnt;
 
+    setup_canvas(rdman, coord);
+
     compute_aggr_of_coord(coord);
 
     /* Clean member shapes. */
@@ -525,8 +596,6 @@
 }
 
 /*! \brief Clean coord_t objects.
- *
- * \todo Make objects can be addin or remove out of coord tree any time.
  */
 static int clean_rdman_coords(redraw_man_t *rdman) {
     coord_t *coord;
@@ -543,7 +612,7 @@
 	    coord = dirty_coords[i];
 	    if(!(coord->flags & COF_DIRTY))
 		continue;
-	    r = clean_coord(coord);
+	    r = clean_coord(rdman, coord);
 	    if(r != OK)
 		return ERR;
 	    /* These two steps can be avoided for drawing all. */
@@ -629,30 +698,30 @@
 }
 #endif
 
-static void draw_shape(redraw_man_t *rdman, shape_t *shape) {
+static void draw_shape(redraw_man_t *rdman, cairo_t *cr, shape_t *shape) {
     paint_t *fill, *stroke;
 
     if(shape->fill || shape->stroke) {
 	switch(shape->sh_type) {
 	case SHT_PATH:
-	    sh_path_draw(shape, rdman->cr);
+	    sh_path_draw(shape, cr);
 	    break;
 	case SHT_TEXT:
-	    sh_text_draw(shape, rdman->cr);
+	    sh_text_draw(shape, cr);
 	    break;
 	case SHT_RECT:
-	    sh_rect_draw(shape, rdman->cr);
+	    sh_rect_draw(shape, cr);
 	    break;
 #ifdef UNITTEST
 	default:
-	    sh_dummy_fill(shape, rdman->cr);
+	    sh_dummy_fill(shape, cr);
 	    break;
 #endif /* UNITTEST */
 	}
 
 	fill = shape->fill;
 	if(shape->fill) {
-	    fill->prepare(fill, rdman->cr);
+	    fill->prepare(fill, cr);
 	    if(shape->stroke)
 		fill_path_preserve(rdman);
 	    else
@@ -661,8 +730,8 @@
 
 	stroke = shape->stroke;
 	if(stroke) {
-	    stroke->prepare(stroke, rdman->cr);
-	    set_shape_stroke_param(shape, rdman->cr);
+	    stroke->prepare(stroke, cr);
+	    set_shape_stroke_param(shape, cr);
 	    stroke_path(rdman);
 	}
     }
@@ -675,6 +744,12 @@
     cairo_paint(cr);
 }
 
+static void clean_canvas_black(cairo_t *cr) {
+    /*! \todo clean to background color. */
+    cairo_set_source_rgba(cr, 0, 0, 0, 0);
+    cairo_paint(cr);
+}
+
 static void make_clip(cairo_t *cr, int n_dirty_areas,
 		      area_t **dirty_areas) {
     int i;
@@ -702,6 +777,9 @@
 static void clean_canvas(cairo_t *cr) {
 }
 
+static void clean_canvas_black(cairo_t *cr) {
+}
+
 static void reset_clip(redraw_man_t *rdman) {
 }
 
@@ -710,26 +788,76 @@
 }
 #endif /* UNITTEST */
 
+static int is_shape_in_areas(shape_t *shape,
+			     int n_areas,
+			     area_t **areas) {
+    int i;
+    geo_t *geo;
+
+    geo = shape->geo;
+    for(i = 0; i < n_areas; i++) {
+	if(is_overlay(geo->cur_area, areas[i]))
+	    return 1;
+    }
+    return 0;
+}
+
+static void update_canvas_2_parent(redraw_man_t *rdman, coord_t *coord) {
+    cairo_t *pcanvas, *canvas;
+    cairo_surface_t *surface;
+
+    if(coord == rdman->root_coord)
+	return;
+
+    canvas = coord->canvas;
+    pcanvas = coord->parent->canvas;
+    surface = cairo_get_target(canvas);
+    cairo_set_source_surface(pcanvas, surface, 0, 0);
+    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) {
+    int dirty = 0;
+    int r;
+    shape_t *member;
+    coord_t *child;
+    cairo_t *canvas;
+    int mem_idx;
+
+    canvas = coord->canvas;
+    member = STAILQ_HEAD(coord->members);
+    mem_idx = 0;
+    child = STAILQ_HEAD(coord->children);
+    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;
+	    child = STAILQ_NEXT(coord_t, sibling, child);
+	} else {
+	    ASSERT(member != NULL);
+	    if(is_shape_in_areas(member, n_areas, areas)) {
+		draw_shape(rdman, canvas, member);
+		dirty = 1;
+	    }
+	    member = STAILQ_NEXT(shape_t, coord_mem_next, member);
+	}
+    }
+
+    if(dirty && coord->flags & COF_OWN_CANVAS) {
+	update_canvas_2_parent(rdman, coord);
+	clean_canvas_black(coord->canvas);
+    }
+
+    return dirty;
+}
+
 static void draw_shapes_in_areas(redraw_man_t *rdman,
 				 int n_areas,
 				 area_t **areas) {
-    geo_t *visit_geo;
-    int i;
-
-    for(visit_geo = STAILQ_HEAD(rdman->all_geos);
-	visit_geo != NULL;
-	visit_geo = STAILQ_NEXT(geo_t, next, visit_geo)) {
-	if(visit_geo->flags & GEF_DIRTY)
-	    clean_shape(visit_geo->shape);
-	if(visit_geo->flags & GEF_HIDDEN)
-	    continue;
-	for(i = 0; i < n_areas; i++) {
-	    if(is_overlay(visit_geo->cur_area, areas[i])) {
-		draw_shape(rdman, visit_geo->shape);
-		break;
-	    }
-	}
-    }
+    draw_coord_shapes_in_areas(rdman, rdman->root_coord, n_areas, areas);
 }
 
 
@@ -810,26 +938,49 @@
  * NOTE: After redrawing, the content must be copied to the backend surface.
  */
 
+/*! \page redraw How to Redraw Shapes?
+ *
+ * Coords are corresponding objects for group tags of SVG files.
+ * In conceptional, every SVG group has a canvas, graphics of child shapes
+ * are drawed into the canvas, applied filters of group, and blended into
+ * canvas of parent of the group.
+ *
+ * But, we don't need to create actually a surface/canvas for every coord.
+ * We only create surface for coords their opacity value are not 1 or they
+ * apply filters on background.  Child shapes of coords without canvas
+ * are drawed on canvas of nearest ancestor which have canvas.  It said
+ * a coord owns a canvas or inherits from an ancestor. (\ref COF_OWN_CANVAS,
+ * clean_coord()) Except, root_coord always owns a canvas.
+ *
+ * \note Default opacity of a coord is 1.
+ *
+ * \sa
+ * - rdman_redraw_all()
+ * - rdman_redraw_changed()
+ * = draw_shapes_in_areas()
+ */
+
 int rdman_redraw_all(redraw_man_t *rdman) {
     geo_t *geo;
+    cairo_surface_t *surface;
+    area_t area;
     int r;
 
-    r = clean_rdman_dirties(rdman);
+    area.x = area.y = 0;
+#ifndef UNITTEST
+    surface = cairo_get_target(rdman->cr);
+    area.w = cairo_image_surface_get_width(surface);
+    area.h = cairo_image_surface_get_height(surface);
+#else
+    area.w = 1024;
+    area.h = 1024;
+#endif
+    add_dirty_area(rdman, &area);
+
+    r = rdman_redraw_changed(rdman);
     if(r != OK)
 	return ERR;
 
-    clean_canvas(rdman->cr);
-
-    for(geo = STAILQ_HEAD(rdman->all_geos);
-	geo != NULL;
-	geo = STAILQ_NEXT(geo_t, next, geo)) {
-	if(geo->flags & GEF_HIDDEN)
-	    continue;
-	draw_shape(rdman, geo->shape);
-    }
-    copy_cr_2_backend(rdman, 0, NULL);
-    rdman->n_dirty_areas = 0;
-
     return OK;
 }