comparison 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
comparison
equal deleted inserted replaced
313:5737548e922f 314:6c350fc92ae3
1 #include <stdio.h> 1 #include <stdio.h>
2 #include <stdlib.h> 2 #include <stdlib.h>
3 #include <string.h> 3 #include <string.h>
4 #include <math.h>
4 #include <cairo.h> 5 #include <cairo.h>
5 #include "mb_types.h" 6 #include "mb_types.h"
6 #include "mb_shapes.h" 7 #include "mb_shapes.h"
7 #include "mb_tools.h" 8 #include "mb_tools.h"
8 #include "mb_redraw_man.h" 9 #include "mb_redraw_man.h"
29 #define sh_get_geo(sh) ((sh)->geo) 30 #define sh_get_geo(sh) ((sh)->geo)
30 #define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0) 31 #define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0)
31 #define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0) 32 #define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0)
32 #define rdman_is_dirty(rdman) \ 33 #define rdman_is_dirty(rdman) \
33 ((rdman)->dirty_coords.num != 0 || \ 34 ((rdman)->dirty_coords.num != 0 || \
34 (rdman)->dirty_geos.num != 0 || \ 35 (rdman)->dirty_geos.num != 0)
35 (rdman)->dirty_areas.num != 0)
36 36
37 #define OK 0 37 #define OK 0
38 #define ERR -1 38 #define ERR -1
39 39
40 #define ARRAY_EXT_SZ 64 40 #define ARRAY_EXT_SZ 64
130 130
131 131
132 static int add_dirty_coord(redraw_man_t *rdman, coord_t *coord) { 132 static int add_dirty_coord(redraw_man_t *rdman, coord_t *coord) {
133 coord->flags |= COF_DIRTY; 133 coord->flags |= COF_DIRTY;
134 ADD_DATA(coords, dirty_coords, coord); 134 ADD_DATA(coords, dirty_coords, coord);
135 return OK;
135 } 136 }
136 137
137 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) { 138 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
138 geo->flags |= GEF_DIRTY; 139 geo->flags |= GEF_DIRTY;
139 ADD_DATA(geos, dirty_geos, geo); 140 ADD_DATA(geos, dirty_geos, geo);
140 } 141 return OK;
141 142 }
142 static int add_dirty_area(redraw_man_t *rdman, area_t *area) { 143
143 ADD_DATA(areas, dirty_areas, area); 144 static int add_dirty_area(redraw_man_t *rdman, coord_t *coord, area_t *area) {
145 int r;
146
147 rdman->n_dirty_areas++;
148 r = areas_add(_coord_get_dirty_areas(coord), area);
149 return r == 0? OK: ERR;
150 }
151
152 static int add_zeroing_coord(redraw_man_t *rdman, coord_t *coord) {
153 coord_set_zeroing(coord);
154 ADD_DATA(coords, zeroing_coords, coord);
155 return OK;
144 } 156 }
145 157
146 static int add_free_obj(redraw_man_t *rdman, void *obj, 158 static int add_free_obj(redraw_man_t *rdman, void *obj,
147 free_func_t free_func) { 159 free_func_t free_func) {
148 int max; 160 int max;
186 poses[0][1] = area->y; 198 poses[0][1] = area->y;
187 poses[1][0] = area->x + area->w; 199 poses[1][0] = area->x + area->w;
188 poses[1][1] = area->y + area->h;; 200 poses[1][1] = area->y + area->h;;
189 } 201 }
190 202
191 static cairo_t *new_canvas(redraw_man_t *rdman) { 203 static cairo_t *canvas_new(int w, int h) {
192 #ifndef UNITTEST 204 #ifndef UNITTEST
205 cairo_surface_t *surface;
193 cairo_t *cr; 206 cairo_t *cr;
194 cairo_surface_t *surface, *cr_surface; 207
195 int w, h;
196
197 cr_surface = cairo_get_target(rdman->cr);
198 w = cairo_image_surface_get_width(cr_surface);
199 h = cairo_image_surface_get_height(cr_surface);
200 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 208 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
201 w, h); 209 w, h);
202 cr = cairo_create(surface); 210 cr = cairo_create(surface);
203 211
204 return cr; 212 return cr;
205 #else 213 #else
206 return NULL; 214 return NULL;
207 #endif 215 #endif
208 } 216 }
209 217
210 static void free_canvas(cairo_t *canvas) { 218 static void canvas_free(cairo_t *canvas) {
211 #ifndef UNITTEST 219 #ifndef UNITTEST
212 cairo_destroy(canvas); 220 cairo_destroy(canvas);
221 #endif
222 }
223
224 static void canvas_get_size(cairo_t *canvas, int *w, int *h) {
225 #ifndef UNITTEST
226 cairo_surface_t *surface;
227
228 surface = cairo_get_target(canvas);
229 *w = cairo_image_surface_get_width(surface);
230 *h = cairo_image_surface_get_height(surface);
231 #else
232 *w = 0;
233 *h = 0;
213 #endif 234 #endif
214 } 235 }
215 236
216 static int geo_off_in_coord(geo_t *geo, coord_t *coord) { 237 static int geo_off_in_coord(geo_t *geo, coord_t *coord) {
217 int off = 0; 238 int off = 0;
244 265
245 RM_MEMBER(coord, geo); 266 RM_MEMBER(coord, geo);
246 coord->num_members--; 267 coord->num_members--;
247 } 268 }
248 269
270 static coord_canvas_info_t *coord_canvas_info_new(redraw_man_t *rdman,
271 coord_t *coord,
272 cairo_t *canvas) {
273 coord_canvas_info_t *info;
274
275 info = (coord_canvas_info_t *)elmpool_elm_alloc(rdman->coord_canvas_pool);
276 if(info == NULL)
277 return info;
278
279 info->owner = coord;
280 info->canvas = canvas;
281 DARRAY_INIT(&info->dirty_areas);
282
283 return info;
284 }
285
286 static void coord_canvas_info_free(redraw_man_t *rdman,
287 coord_canvas_info_t *info) {
288 DARRAY_DESTROY(&info->dirty_areas);
289 elmpool_elm_free(rdman->coord_canvas_pool, info);
290 }
291
249 static void mouse_event_root_dummy(event_t *evt, void *arg) { 292 static void mouse_event_root_dummy(event_t *evt, void *arg) {
250 } 293 }
251 294
252 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr, cairo_t *backend) { 295 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr, cairo_t *backend) {
253 extern void redraw_man_destroy(redraw_man_t *rdman); 296 extern void redraw_man_destroy(redraw_man_t *rdman);
255 observer_t *addrm_ob; 298 observer_t *addrm_ob;
256 extern void addrm_monitor_hdlr(event_t *evt, void *arg); 299 extern void addrm_monitor_hdlr(event_t *evt, void *arg);
257 300
258 memset(rdman, 0, sizeof(redraw_man_t)); 301 memset(rdman, 0, sizeof(redraw_man_t));
259 302
303 DARRAY_INIT(&rdman->dirty_coords);
304 DARRAY_INIT(&rdman->dirty_geos);
305 DARRAY_INIT(&rdman->gen_geos);
306 DARRAY_INIT(&rdman->zeroing_coords);
307
260 rdman->geo_pool = elmpool_new(sizeof(geo_t), 128); 308 rdman->geo_pool = elmpool_new(sizeof(geo_t), 128);
261 rdman->coord_pool = elmpool_new(sizeof(coord_t), 16); 309 rdman->coord_pool = elmpool_new(sizeof(coord_t), 16);
262 rdman->shnode_pool = elmpool_new(sizeof(shnode_t), 16); 310 rdman->shnode_pool = elmpool_new(sizeof(shnode_t), 16);
263 rdman->observer_pool = elmpool_new(sizeof(observer_t), 32); 311 rdman->observer_pool = elmpool_new(sizeof(observer_t), 32);
264 rdman->subject_pool = elmpool_new(sizeof(subject_t), 32); 312 rdman->subject_pool = elmpool_new(sizeof(subject_t), 32);
265 rdman->paint_color_pool = elmpool_new(_paint_color_size, 64); 313 rdman->paint_color_pool = elmpool_new(_paint_color_size, 64);
266 rdman->pent_pool = elmpool_new(sizeof(mb_prop_entry_t), 128); 314 rdman->pent_pool = elmpool_new(sizeof(mb_prop_entry_t), 128);
315 rdman->coord_canvas_pool = elmpool_new(sizeof(coord_canvas_info_t), 16);
267 if(!(rdman->geo_pool && rdman->coord_pool && rdman->shnode_pool && 316 if(!(rdman->geo_pool && rdman->coord_pool && rdman->shnode_pool &&
268 rdman->observer_pool && rdman->subject_pool && 317 rdman->observer_pool && rdman->subject_pool &&
269 rdman->paint_color_pool)) 318 rdman->paint_color_pool && rdman->coord_canvas_pool))
270 goto err; 319 goto err;
271 320
272 rdman->ob_factory.subject_alloc = ob_subject_alloc; 321 rdman->ob_factory.subject_alloc = ob_subject_alloc;
273 rdman->ob_factory.subject_free = ob_subject_free; 322 rdman->ob_factory.subject_free = ob_subject_free;
274 rdman->ob_factory.observer_alloc = ob_observer_alloc; 323 rdman->ob_factory.observer_alloc = ob_observer_alloc;
281 subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN); 330 subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
282 if(!(rdman->redraw && rdman->addrm_monitor)) 331 if(!(rdman->redraw && rdman->addrm_monitor))
283 goto err; 332 goto err;
284 333
285 addrm_ob = subject_add_observer(rdman->addrm_monitor, 334 addrm_ob = subject_add_observer(rdman->addrm_monitor,
286 addrm_monitor_hdlr, NULL); 335 addrm_monitor_hdlr, rdman);
287 if(addrm_ob == NULL) 336 if(addrm_ob == NULL)
288 goto err; 337 goto err;
289 338
290 rdman->last_mouse_over = NULL; 339 rdman->last_mouse_over = NULL;
291 340
297 mb_prop_store_init(&rdman->root_coord->obj.props, rdman->pent_pool); 346 mb_prop_store_init(&rdman->root_coord->obj.props, rdman->pent_pool);
298 rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory, 347 rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory,
299 rdman->root_coord, 348 rdman->root_coord,
300 OBJT_COORD); 349 OBJT_COORD);
301 rdman->root_coord->flags |= COF_OWN_CANVAS; 350 rdman->root_coord->flags |= COF_OWN_CANVAS;
302 rdman->root_coord->canvas = cr; 351 rdman->root_coord->canvas_info =
352 coord_canvas_info_new(rdman, rdman->root_coord, cr);
303 rdman->root_coord->opacity = 1; 353 rdman->root_coord->opacity = 1;
304 354
305 rdman->cr = cr; 355 rdman->cr = cr;
306 rdman->backend = backend; 356 rdman->backend = backend;
307 357
332 elmpool_free(rdman->subject_pool); 382 elmpool_free(rdman->subject_pool);
333 if(rdman->paint_color_pool) 383 if(rdman->paint_color_pool)
334 elmpool_free(rdman->paint_color_pool); 384 elmpool_free(rdman->paint_color_pool);
335 if(rdman->pent_pool) 385 if(rdman->pent_pool)
336 elmpool_free(rdman->pent_pool); 386 elmpool_free(rdman->pent_pool);
387 if(rdman->coord_canvas_pool)
388 elmpool_free(rdman->coord_canvas_pool);
389 DARRAY_DESTROY(&rdman->dirty_coords);
390 DARRAY_DESTROY(&rdman->dirty_geos);
391 DARRAY_DESTROY(&rdman->gen_geos);
392 DARRAY_DESTROY(&rdman->zeroing_coords);
337 return ERR; 393 return ERR;
338 } 394 }
339 395
340 void redraw_man_destroy(redraw_man_t *rdman) { 396 void redraw_man_destroy(redraw_man_t *rdman) {
341 coord_t *coord, *saved_coord; 397 coord_t *coord, *saved_coord;
373 #endif 429 #endif
374 saved_shape = shape; 430 saved_shape = shape;
375 } 431 }
376 if(saved_shape != NULL) 432 if(saved_shape != NULL)
377 rdman_shape_free(rdman, saved_shape); 433 rdman_shape_free(rdman, saved_shape);
378 434
435 coord_canvas_info_free(rdman, rdman->root_coord->canvas_info);
436
379 elmpool_free(rdman->coord_pool); 437 elmpool_free(rdman->coord_pool);
380 elmpool_free(rdman->geo_pool); 438 elmpool_free(rdman->geo_pool);
381 elmpool_free(rdman->shnode_pool); 439 elmpool_free(rdman->shnode_pool);
382 elmpool_free(rdman->observer_pool); 440 elmpool_free(rdman->observer_pool);
383 elmpool_free(rdman->subject_pool); 441 elmpool_free(rdman->subject_pool);
384 elmpool_free(rdman->paint_color_pool); 442 elmpool_free(rdman->paint_color_pool);
385 elmpool_free(rdman->pent_pool); 443 elmpool_free(rdman->pent_pool);
444 elmpool_free(rdman->coord_canvas_pool);
386 445
387 DARRAY_DESTROY(&rdman->dirty_coords); 446 DARRAY_DESTROY(&rdman->dirty_coords);
388 DARRAY_DESTROY(&rdman->dirty_geos); 447 DARRAY_DESTROY(&rdman->dirty_geos);
389 DARRAY_DESTROY(&rdman->dirty_areas);
390 DARRAY_DESTROY(&rdman->gen_geos); 448 DARRAY_DESTROY(&rdman->gen_geos);
449 DARRAY_DESTROY(&rdman->zeroing_coords);
391 } 450 }
392 451
393 452
394 #define ASSERT(x) 453 #define ASSERT(x)
395 /* 454 /*
556 OBJT_COORD); 615 OBJT_COORD);
557 subject_set_monitor(coord->mouse_event, rdman->addrm_monitor); 616 subject_set_monitor(coord->mouse_event, rdman->addrm_monitor);
558 /*! \note default opacity == 1 */ 617 /*! \note default opacity == 1 */
559 coord->opacity = 1; 618 coord->opacity = 1;
560 if(parent) 619 if(parent)
561 coord->canvas = parent->canvas; 620 coord->canvas_info = parent->canvas_info;
562 rdman->n_coords++; 621 rdman->n_coords++;
563 622
564 coord->order = ++rdman->next_coord_order; 623 coord->order = ++rdman->next_coord_order;
565 if(coord->order == 0) { 624 if(coord->order == 0) {
566 rdman->next_coord_order = 0; 625 rdman->next_coord_order = 0;
634 } 693 }
635 694
636 if(cm_cnt || rdman_is_dirty(rdman)) 695 if(cm_cnt || rdman_is_dirty(rdman))
637 return rdman_coord_free_postponse(rdman, coord); 696 return rdman_coord_free_postponse(rdman, coord);
638 697
639 /* Free canvas (\ref redraw) */ 698 /* Free canvas and canvas_info (\ref redraw) */
640 if(coord->flags & COF_OWN_CANVAS) 699 if(coord->flags & COF_OWN_CANVAS) {
641 free_canvas(coord->canvas); 700 canvas_free(_coord_get_canvas(coord));
701 coord_canvas_info_free(rdman, coord->canvas_info);
702 }
642 703
643 RM_CHILD(parent, coord); 704 RM_CHILD(parent, coord);
644 subject_free(coord->mouse_event); 705 subject_free(coord->mouse_event);
645 mb_prop_store_destroy(&coord->obj.props); 706 mb_prop_store_destroy(&coord->obj.props);
646 elmpool_elm_free(rdman->coord_pool, coord); 707 elmpool_elm_free(rdman->coord_pool, coord);
730 preorder_coord_skip_subtree(child); 791 preorder_coord_skip_subtree(child);
731 continue; 792 continue;
732 } 793 }
733 794
734 add_dirty_coord(rdman, child); 795 add_dirty_coord(rdman, child);
796
797 if(child->flags & COF_OWN_CANVAS) {
798 preorder_coord_skip_subtree(child);
799 continue;
800 }
735 } 801 }
736 802
737 return OK; 803 return OK;
738 } 804 }
739 805
811 sh_hide(shape); 877 sh_hide(shape);
812 else 878 else
813 sh_show(shape); 879 sh_show(shape);
814 } 880 }
815 881
816 /*! \brief Setup canvas for the coord. 882 /*! \brief Setup canvas_info for the coord.
817 * 883 *
818 * Own a canvas or inherit it from parent. 884 * Own a canvas or inherit it from parent.
819 * \sa 885 * \sa
820 * - \ref redraw 886 * - \ref redraw
821 */ 887 */
822 static void setup_canvas(redraw_man_t *rdman, coord_t *coord) { 888 static void setup_canvas_info(redraw_man_t *rdman, coord_t *coord) {
823 if(coord->parent == NULL) 889 if(coord->parent == NULL)
824 return; 890 return;
825 891
826 if(coord->opacity != 1) { 892 if(coord->opacity != 1 || coord_is_cached(coord)) {
827 if(!(coord->flags & COF_OWN_CANVAS)) { 893 if(!(coord->flags & COF_OWN_CANVAS)) {
828 coord->canvas = new_canvas(rdman); 894 /* canvas is assigned latter, in zeroing_coord() */
895 coord->canvas_info = coord_canvas_info_new(rdman, coord, NULL);
829 coord->flags |= COF_OWN_CANVAS; 896 coord->flags |= COF_OWN_CANVAS;
830 } 897 }
831 } else { 898 } else {
832 if(coord->flags & COF_OWN_CANVAS) { 899 if(coord->flags & COF_OWN_CANVAS) {
833 free_canvas(coord->canvas); 900 canvas_free(_coord_get_canvas(coord));
901 coord_canvas_info_free(rdman, coord->canvas_info);
834 coord->flags &= ~COF_OWN_CANVAS; 902 coord->flags &= ~COF_OWN_CANVAS;
835 } 903 }
836 coord->canvas = coord->parent->canvas; 904 /* This must here to keep coords that do not own canvas
837 } 905 * can always point to right canvas_info. Since, they
838 } 906 * don't know when will parent change it's canvas_info.
839 907 */
840 static int clean_coord(redraw_man_t *rdman, coord_t *coord) { 908 coord->canvas_info = coord->parent->canvas_info;
909 }
910 }
911
912 static int coord_clean_members_n_compute_area(coord_t *coord) {
841 geo_t *geo; 913 geo_t *geo;
842 /*! \note poses is shared by invokings, it is not support reentrying. */ 914 /*! \note poses is shared by invokings, it is not support reentrying. */
843 static co_aix (*poses)[2]; 915 static co_aix (*poses)[2];
844 static int max_poses = 0; 916 static int max_poses = 0;
845 int cnt, pos_cnt; 917 int cnt, pos_cnt;
846 918
847 setup_canvas(rdman, coord);
848
849 compute_aggr_of_coord(coord);
850
851 /* Clean member shapes. */ 919 /* Clean member shapes. */
852 cnt = 0; 920 cnt = 0;
853 FORMEMBERS(coord, geo) { 921 FORMEMBERS(coord, geo) {
854 SWAP(geo->cur_area, geo->last_area, area_t *);
855 clean_shape(geo->shape); 922 clean_shape(geo->shape);
856 cnt++; 923 cnt++;
857 } 924 }
858 925
859 if(max_poses < (cnt * 2)) { 926 if(max_poses < (cnt * 2)) {
869 FORMEMBERS(coord, geo) { 936 FORMEMBERS(coord, geo) {
870 area_to_positions(geo->cur_area, poses + pos_cnt); 937 area_to_positions(geo->cur_area, poses + pos_cnt);
871 pos_cnt += 2; 938 pos_cnt += 2;
872 } 939 }
873 940
874 SWAP(coord->cur_area, coord->last_area, area_t *);
875 area_init(coord->cur_area, pos_cnt, poses); 941 area_init(coord->cur_area, pos_cnt, poses);
876 942
943 return OK;
944 }
945
946 /*! \brief Shift space of coord to align left-top of minimum covering.
947 *
948 * Align left-top of minimum rectangle covering occupied area of
949 * sub-graphic to origin of the space.
950 */
951 static
952 void zeroing_coord(redraw_man_t *rdman, coord_t *coord) {
953 coord_t *cur;
954 area_t *area;
955 co_aix min_x, min_y;
956 co_aix max_x, max_y;
957 co_aix x, y;
958 int w, h;
959 int c_w, c_h;
960 cairo_t *canvas;
961 co_aix *aggr;
962 co_aix poses[2][2];
963
964 if(coord->parent == NULL) /*! \note Should not zeroing root coord */
965 abort();
966 if(!(coord_is_zeroing(coord)))
967 abort();
968
969 coord_clear_zeroing(coord);
970
971 /*
972 * Compute minimum overing area of sub-graphic
973 */
974 area = coord_get_area(coord);
975 min_x = area->x;
976 min_y = area->y;
977 max_x = min_x + area->w;
978 max_y = min_y + area->h;
979
980 FOR_COORDS_PREORDER(coord, cur) {
981 area = coord_get_area(cur);
982 if(area->x < min_x)
983 min_x = area->x;
984 if(area->y < min_y)
985 min_y = area->y;
986
987 x = area->x + area->w;
988 y = area->y + area->h;
989
990 if(x > max_x)
991 max_x = x;
992 if(y > max_y)
993 max_y = y;
994 if(cur->flags & COF_OWN_CANVAS)
995 preorder_coord_skip_subtree(cur);
996 }
997
998 w = max_x - min_x;
999 h = max_y - min_y;
1000
1001 /*
1002 * Setup area of the coord
1003 */
1004 aggr = coord_get_aggr_matrix(coord);
1005 x = y = 0;
1006 coord_trans_pos(coord->parent, &x, &y);
1007 poses[0][0] = x;
1008 poses[0][1] = y;
1009 x = w;
1010 y = h;
1011 coord_trans_pos(coord->parent, &x, &y);
1012 poses[1][0] = x;
1013 poses[1][1] = y;
1014
1015 area_init(coord_get_area(coord), 2, poses);
1016
1017 canvas = _coord_get_canvas(coord);
1018 if(canvas)
1019 canvas_get_size(canvas, &c_w, &c_h);
1020 else
1021 c_w = c_h = 0;
1022
1023 if(!coord_get_flags(coord, COF_JUST_CLEAN) &&
1024 min_x >= 0 && min_y >= 0 && max_x <= c_w && max_y <= c_h)
1025 /* Canvas fully cover sub-graphic. */
1026 return;
1027
1028 /*
1029 * Adjust matrics of descendants to align left-top corner of
1030 * minimum covering area with origin of space defined by
1031 * zeroing coord.
1032 */
1033 FOR_COORDS_PREORDER(coord, cur) {
1034 aggr = coord_get_aggr_matrix(cur);
1035 aggr[3] -= min_x;
1036 aggr[5] -= min_y;
1037 if(coord_get_flags(cur, COF_OWN_CANVAS)) {
1038 /*
1039 * Coords, zeroing, is zeroed in preorder of tree.
1040 * So, they are zeroed after ancesters with correctly
1041 * coord_t::aggr_matrix of parent coord to zeroing.
1042 */
1043 preorder_coord_skip_subtree(cur);
1044 area = coord_get_area(cur);
1045 area->x -= min_x;
1046 area->y -= min_y;
1047 } else
1048 coord_clean_members_n_compute_area(cur);
1049 }
1050
1051 /*
1052 * Setup canvas
1053 */
1054 if(canvas == NULL || w > c_w || h > c_w) {
1055 if(canvas)
1056 canvas_free(canvas);
1057 canvas = canvas_new(w, h);
1058 _coord_set_canvas(coord, canvas);
1059 }
1060
1061 area = &coord->canvas_info->cached_dirty_area;
1062 area->x = 0;
1063 area->y = 0;
1064 area->w = w;
1065 area->h = h;
1066 DARRAY_CLEAN(_coord_get_dirty_areas(coord));
1067 add_dirty_area(rdman, coord, area);
1068 }
1069
1070 /*! \brief Clean dirty coords.
1071 *
1072 * \note coords their opacity != 1 are also traded as cached ones.
1073 */
1074 static int clean_coord(redraw_man_t *rdman, coord_t *coord) {
1075 int r;
1076
1077 setup_canvas_info(rdman, coord);
1078
1079 if(coord->flags & COF_OWN_CANVAS)
1080 compute_aggr_of_cached_coord(coord);
1081 else
1082 compute_aggr_of_coord(coord);
1083
1084 r = coord_clean_members_n_compute_area(coord);
1085 if(r != OK)
1086 return ERR;
1087
877 coord->flags &= ~COF_DIRTY; 1088 coord->flags &= ~COF_DIRTY;
878 1089
879 return OK; 1090 return OK;
880 } 1091 }
881 1092
898 continue; 1109 continue;
899 r = clean_coord(rdman, coord); 1110 r = clean_coord(rdman, coord);
900 if(r != OK) 1111 if(r != OK)
901 return ERR; 1112 return ERR;
902 /* These two steps can be avoided for drawing all. */ 1113 /* These two steps can be avoided for drawing all. */
903 add_dirty_area(rdman, &coord->areas[0]); 1114 add_dirty_area(rdman, coord, &coord->areas[0]);
904 add_dirty_area(rdman, &coord->areas[1]); 1115 add_dirty_area(rdman, coord, &coord->areas[1]);
905 } 1116 }
906 rdman->dirty_coords.num = 0;
907 } 1117 }
908 return OK; 1118 return OK;
909 } 1119 }
910 1120
911 static int clean_rdman_geos(redraw_man_t *rdman) { 1121 static int clean_rdman_geos(redraw_man_t *rdman) {
912 int i; 1122 int i;
913 int n_dirty_geos; 1123 int n_dirty_geos;
914 geo_t **dirty_geos; 1124 geo_t **dirty_geos;
915 geo_t *visit_geo; 1125 geo_t *visit_geo;
1126 coord_t *coord;
916 1127
917 n_dirty_geos = rdman->dirty_geos.num; 1128 n_dirty_geos = rdman->dirty_geos.num;
918 if(n_dirty_geos > 0) { 1129 if(n_dirty_geos > 0) {
919 dirty_geos = rdman->dirty_geos.ds; 1130 dirty_geos = rdman->dirty_geos.ds;
920 for(i = 0; i < n_dirty_geos; i++) { 1131 for(i = 0; i < n_dirty_geos; i++) {
921 visit_geo = dirty_geos[i]; 1132 visit_geo = dirty_geos[i];
922 if(!(visit_geo->flags & GEF_DIRTY)) 1133 if(!(visit_geo->flags & GEF_DIRTY))
923 continue; 1134 continue;
924 1135
925 SWAP(visit_geo->cur_area, visit_geo->last_area, area_t *);
926 clean_shape(visit_geo->shape); 1136 clean_shape(visit_geo->shape);
927 add_dirty_area(rdman, visit_geo->cur_area); 1137 coord = geo_get_coord(visit_geo);
928 add_dirty_area(rdman, visit_geo->last_area); 1138 add_dirty_area(rdman, coord, visit_geo->cur_area);
929 } 1139 add_dirty_area(rdman, coord, visit_geo->last_area);
930 rdman->dirty_geos.num = 0; 1140 }
931 } 1141 }
1142
1143 return OK;
1144 }
1145
1146 /*! \brief Add canvas owner of dirty geos to coord_t::zeroing_coords.
1147 *
1148 * All possible coords that need a zeroing have at least one dirty geo.
1149 */
1150 static int add_rdman_zeroing_coords(redraw_man_t *rdman) {
1151 int i;
1152 int n_dirty_geos;
1153 geo_t **dirty_geos, *geo;
1154 int n_dirty_coords;
1155 coord_t **dirty_coords, *coord;
1156
1157 n_dirty_geos = rdman->dirty_geos.num;
1158 dirty_geos = rdman->dirty_geos.ds;
1159 for(i = 0; i < n_dirty_geos; i++) {
1160 geo = dirty_geos[i];
1161 coord = geo_get_coord(geo)->canvas_info->owner;
1162 while(!coord_get_flags(coord, COF_MUST_ZEROING | COF_TEMP_MARK)) {
1163 coord_set_flags(coord, COF_TEMP_MARK);
1164 if(coord_is_root(coord))
1165 break;
1166 coord = coord->parent->canvas_info->owner;
1167 }
1168 }
1169
1170 n_dirty_coords = rdman->dirty_coords.num;
1171 dirty_coords = rdman->dirty_coords.ds;
1172 for(i = 0; i < n_dirty_coords; i++) {
1173 coord = dirty_coords[i]->canvas_info->owner;
1174 while(!coord_get_flags(coord, COF_MUST_ZEROING | COF_TEMP_MARK)) {
1175 coord_set_flags(coord, COF_TEMP_MARK);
1176 if(coord_is_root(coord))
1177 break;
1178 coord = coord->parent->canvas_info->owner;
1179 }
1180 }
1181
1182 FOR_COORDS_PREORDER(rdman->root_coord, coord) {
1183 if(!coord_get_flags(coord, COF_TEMP_MARK)) {
1184 preorder_coord_skip_subtree(coord);
1185 continue;
1186 }
1187 add_zeroing_coord(rdman, coord);
1188 coord_clear_flags(coord, COF_TEMP_MARK);
1189 }
1190
1191 return OK;
1192 }
1193
1194 /*! \brief Zeroing coords in redraw_man_t::zeroing_coords.
1195 *
1196 * \note redraw_man_t::zeroing_coords must in ascent partial order of tree.
1197 */
1198 static int zeroing_rdman_coords(redraw_man_t *rdman) {
1199 int i;
1200 coords_t *all_zeroing;
1201 coord_t *coord;
1202
1203 all_zeroing = &rdman->zeroing_coords;
1204 for(i = all_zeroing->num - 1; i >= 0; i--) {
1205 coord = all_zeroing->ds[i];
1206 if(coord_is_root(coord))
1207 continue;
1208 zeroing_coord(rdman, coord);
1209 }
1210
1211 return OK;
1212 }
1213
1214 /* \brief Compute matrix from cached canvas to parent device space.
1215 */
1216 static void compute_cached_2_pdev_matrix(coord_t *coord,
1217 co_aix canvas2pdev_matrix[6]) {
1218 coord_t *parent;
1219 co_aix *aggr;
1220 co_aix *matrix, *paggr;
1221 co_aix scale_x, scale_y;
1222 co_aix shift_x, shift_y;
1223 co_aix canvas2p[6];
1224
1225 aggr = coord_get_aggr_matrix(coord);
1226 matrix = coord->matrix;
1227 parent = coord->parent;
1228 paggr = coord_get_aggr_matrix(parent);
1229
1230 scale_x = matrix[0] / aggr[0];
1231 scale_y = matrix[3] / aggr[3];
1232 shift_x = matrix[2] - scale_x * aggr[2];
1233 shift_y = matrix[5] - scale_y * aggr[5];
1234
1235 canvas2p[0] = scale_x;
1236 canvas2p[1] = 0;
1237 canvas2p[2] = shift_x;
1238 canvas2p[3] = 0;
1239 canvas2p[4] = scale_y;
1240 canvas2p[5] = shift_y;
1241
1242 matrix_mul(paggr, canvas2p, canvas2pdev_matrix);
1243 }
1244
1245 /*! \brief Add aggregated dirty areas to ancestor.
1246 *
1247 * Dirty areas are aggregated into two areas. It assumes that even or odd
1248 * ones are old areas or new areas repsective. So, all even ones are
1249 * aggregated in an area, and odd ones are in another.
1250 */
1251 static void add_aggr_dirty_areas_to_ancestor(redraw_man_t *rdman,
1252 coord_t *coord) {
1253 int i;
1254 int n_areas;
1255 co_aix poses0[2][2], poses1[2][2];
1256 co_aix reverse[6];
1257 co_aix canvas2pdev_matrix[6];
1258 area_t **areas, *area;
1259 area_t *area0, *area1;
1260 coord_t *parent, *pcached_coord;
1261
1262 n_areas = _coord_get_dirty_areas(coord)->num;
1263 areas = _coord_get_dirty_areas(coord)->ds;
1264 if(n_areas == 0)
1265 abort(); /* should not happen! */
1266
1267 area0 = coord->canvas_info->aggr_dirty_areas;
1268 area1 = area0 + 1;
1269
1270 for(i = 0; i < n_areas; i++) {
1271 area = areas[i];
1272 if(area->w != 0 || area->h != 0)
1273 break;
1274 }
1275
1276 if(i < n_areas) {
1277 area = areas[i++];
1278 poses0[0][0] = area->x;
1279 poses0[0][1] = area->y;
1280 poses0[1][0] = area->x + area->w;
1281 poses0[1][1] = area->y + area->h;
1282 } else {
1283 poses0[0][0] = 0;
1284 poses0[0][1] = 0;
1285 poses0[1][0] = 0;
1286 poses0[1][1] = 0;
1287 }
1288
1289 if(i < n_areas) {
1290 area = areas[i++];
1291 poses1[0][0] = area->x;
1292 poses1[0][1] = area->y;
1293 poses1[1][0] = area->x + area->w;
1294 poses1[1][1] = area->y + area->h;
1295 } else {
1296 poses1[0][0] = 0;
1297 poses1[0][1] = 0;
1298 poses1[1][0] = 0;
1299 poses1[1][1] = 0;
1300 }
1301
1302 for(; i < n_areas - 1;) {
1303 /* Even areas */
1304 area = areas[i++];
1305 if(area->w != 0 || area->h != 0) {
1306 poses0[0][0] = MIN(poses0[0][0], area->x);
1307 poses0[0][1] = MIN(poses0[0][1], area->y);
1308 poses0[1][0] = MAX(poses0[1][0], area->x + area->w);
1309 poses0[1][1] = MAX(poses0[1][1], area->y + area->h);
1310 }
1311 /* Odd areas */
1312 area = areas[i++];
1313 if(area->w != 0 || area->h != 0) {
1314 poses1[0][0] = MIN(poses1[0][0], area->x);
1315 poses1[0][1] = MIN(poses1[0][1], area->y);
1316 poses1[1][0] = MAX(poses1[1][0], area->x + area->w);
1317 poses1[1][1] = MAX(poses1[1][1], area->y + area->h);
1318 }
1319 }
1320
1321 if(i < n_areas) {
1322 area = areas[i];
1323 if(area->w != 0 || area->h != 0) {
1324 poses0[0][0] = MIN(poses0[0][0], area->x);
1325 poses0[0][1] = MIN(poses0[0][1], area->y);
1326 poses0[1][0] = MAX(poses0[1][0], area->x + area->w);
1327 poses0[1][1] = MAX(poses0[1][1], area->y + area->h);
1328 }
1329 }
1330
1331 parent = coord->parent;
1332 pcached_coord = parent->canvas_info->owner;
1333
1334 compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
1335
1336 matrix_trans_pos(canvas2pdev_matrix, poses0[0], poses0[0] + 1);
1337 matrix_trans_pos(canvas2pdev_matrix, poses0[1], poses0[1] + 1);
1338 area_init(area0, 2, poses0);
1339 add_dirty_area(rdman, pcached_coord, area0);
1340
1341 matrix_trans_pos(canvas2pdev_matrix, poses1[0], poses1[0] + 1);
1342 matrix_trans_pos(canvas2pdev_matrix, poses1[1], poses1[1] + 1);
1343 area_init(area1, 2, poses1);
1344 if(area1->w != 0 || area1->h != 0)
1345 add_dirty_area(rdman, pcached_coord, area1);
1346
1347 if(coord_get_flags(coord, COF_JUST_CLEAN) &&
1348 !coord_get_flags(pcached_coord, COF_JUST_CLEAN))
1349 add_dirty_area(rdman, pcached_coord, coord->last_area);
1350 }
1351
1352 static int add_rdman_aggr_dirty_areas(redraw_man_t *rdman) {
1353 int i;
1354 int n_zeroing;
1355 coord_t **zeroings;
1356 coord_t *coord;
1357
1358 n_zeroing = rdman->zeroing_coords.num;
1359 zeroings = rdman->zeroing_coords.ds;
1360 for(i = n_zeroing - 1; i >= 0; i--) {
1361 coord = zeroings[i];
1362 if(!coord_is_root(coord))
1363 add_aggr_dirty_areas_to_ancestor(rdman, coord);
1364 }
1365
1366 return OK;
1367 }
1368
1369 static int add_rdman_cached_dirty_areas(redraw_man_t *rdman) {
1370 int i;
1371 coord_t *coord, **dirty_coords;
1372 int n_dirty_coords;
1373
1374 n_dirty_coords = rdman->dirty_coords.num;
1375 dirty_coords = rdman->dirty_coords.ds;
1376 for(i = 0; i < n_dirty_coords; i++) {
1377 coord = dirty_coords[i];
1378 if(coord_get_flags(coord, COF_OWN_CANVAS)) {
1379 add_dirty_area(rdman, coord, coord->cur_area);
1380 add_dirty_area(rdman, coord, coord->last_area);
1381 }
1382 }
932 1383
933 return OK; 1384 return OK;
934 } 1385 }
935 1386
936 static int clean_rdman_dirties(redraw_man_t *rdman) { 1387 static int clean_rdman_dirties(redraw_man_t *rdman) {
937 int r; 1388 int r;
1389 int i;
1390 coord_t **coords;
1391 geo_t **geos;
1392
1393 coords = rdman->dirty_coords.ds;
1394 for(i = 0; i < rdman->dirty_coords.num; i++)
1395 if(coords[i]->flags & COF_DIRTY)
1396 SWAP(coords[i]->cur_area, coords[i]->last_area, area_t *);
1397
1398 geos = rdman->dirty_geos.ds;
1399 for(i = 0; i < rdman->dirty_geos.num; i++)
1400 if(geos[i]->flags & GEF_DIRTY)
1401 SWAP(geos[i]->cur_area, geos[i]->last_area, area_t *);
938 1402
939 r = clean_rdman_coords(rdman); 1403 r = clean_rdman_coords(rdman);
940 if(r != OK) 1404 if(r != OK)
941 return ERR; 1405 return ERR;
1406
1407 for(i = 0; i < rdman->dirty_coords.num; i++)
1408 coord_set_flags(rdman->dirty_coords.ds[i], COF_JUST_CLEAN);
942 1409
943 r = clean_rdman_geos(rdman); 1410 r = clean_rdman_geos(rdman);
944 if(r != OK) 1411 if(r != OK)
945 return ERR; 1412 return ERR;
946 1413
1414 r = add_rdman_zeroing_coords(rdman);
1415 if(r != OK)
1416 return ERR;
1417
1418 r = zeroing_rdman_coords(rdman);
1419 if(r != OK)
1420 return ERR;
1421
1422 r = add_rdman_aggr_dirty_areas(rdman);
1423 if(r != OK)
1424 return ERR;
1425
1426 r = add_rdman_cached_dirty_areas(rdman);
1427 if(r != OK)
1428 return ERR;
1429
1430 for(i = 0; i < rdman->dirty_coords.num; i++)
1431 coord_clear_flags(rdman->dirty_coords.ds[i], COF_JUST_CLEAN);
1432
947 return OK; 1433 return OK;
948 } 1434 }
949 1435
950 1436
951 /* Drawing and Redrawing 1437 /* Drawing and Redrawing
1026 } 1512 }
1027 } 1513 }
1028 } 1514 }
1029 1515
1030 #ifndef UNITTEST 1516 #ifndef UNITTEST
1517 static void clear_canvas(canvas_t *canvas) {
1518 cairo_operator_t old_op;
1519
1520 #if 1
1521 old_op = cairo_get_operator(canvas);
1522 cairo_set_operator(canvas, CAIRO_OPERATOR_CLEAR);
1523 cairo_paint(canvas);
1524 cairo_set_operator(canvas, old_op);
1525 #else
1526 cairo_set_source_rgba(canvas, 0, 0, 0, 0);
1527 cairo_paint(canvas);
1528 #endif
1529 }
1530
1031 static void clean_canvas(cairo_t *cr, co_aix w, co_aix h) { 1531 static void clean_canvas(cairo_t *cr, co_aix w, co_aix h) {
1532 cairo_operator_t saved_op;
1533
1534 saved_op = cairo_get_operator(cr);
1535 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1536
1032 /*! \todo clean to background color. */ 1537 /*! \todo clean to background color. */
1033 cairo_set_source_rgb(cr, 1, 1, 1); 1538 cairo_set_source_rgba(cr, 1, 1, 1, 1);
1034 #if 1 1539
1035 /* For some unknown reasons, cairo_paint() can not erease 1540 /* For some unknown reasons, cairo_paint() can not erease
1036 * painted graphic cleanly. So, cairo_fill() are used to 1541 * painted graphic cleanly. So, cairo_fill() are used to
1037 * replace it. 1542 * replace it.
1038 */ 1543 */
1039 cairo_rectangle(cr, 0, 0, w, h); 1544 cairo_rectangle(cr, 0, 0, w, h);
1040 cairo_fill(cr); 1545 cairo_fill(cr);
1041 #else 1546
1042 cairo_paint(cr); 1547 cairo_set_operator(cr, saved_op);
1043 #endif
1044 } 1548 }
1045 1549
1046 static void clean_canvas_black(cairo_t *cr, co_aix w, co_aix h) { 1550 static void clean_canvas_black(cairo_t *cr, co_aix w, co_aix h) {
1551 clear_canvas(cr);
1552
1047 /*! \todo clean to background color. */ 1553 /*! \todo clean to background color. */
1048 cairo_set_source_rgba(cr, 0, 0, 0, 0); 1554 cairo_set_source_rgba(cr, 0, 0, 0, 0);
1049 cairo_paint(cr); 1555 cairo_paint(cr);
1050 } 1556 }
1051 1557
1059 cairo_rectangle(cr, area->x, area->y, area->w, area->h); 1565 cairo_rectangle(cr, area->x, area->y, area->w, area->h);
1060 } 1566 }
1061 cairo_clip(cr); 1567 cairo_clip(cr);
1062 } 1568 }
1063 1569
1064 static void reset_clip(redraw_man_t *rdman) { 1570 static void reset_clip(canvas_t *cr) {
1065 cairo_reset_clip(rdman->backend); 1571 cairo_reset_clip(cr);
1066 } 1572 }
1067 1573
1068 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas, 1574 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
1069 area_t **dirty_areas) { 1575 area_t **dirty_areas) {
1576 cairo_operator_t saved_op;
1577
1070 if(n_dirty_areas) 1578 if(n_dirty_areas)
1071 make_clip(rdman->backend, n_dirty_areas, dirty_areas); 1579 make_clip(rdman->backend, n_dirty_areas, dirty_areas);
1072 1580
1581 saved_op = cairo_get_operator(rdman->backend);
1582 cairo_set_operator(rdman->backend, CAIRO_OPERATOR_SOURCE);
1073 cairo_paint(rdman->backend); 1583 cairo_paint(rdman->backend);
1584 cairo_set_operator(rdman->backend, saved_op);
1074 } 1585 }
1075 #else /* UNITTEST */ 1586 #else /* UNITTEST */
1076 static void clean_canvas(cairo_t *cr, co_aix w, co_aix h) { 1587 static void clean_canvas(cairo_t *cr, co_aix w, co_aix h) {
1077 } 1588 }
1078 1589
1079 static void clean_canvas_black(cairo_t *cr, co_aix w, co_aix h) { 1590 static void clean_canvas_black(cairo_t *cr, co_aix w, co_aix h) {
1080 } 1591 }
1081 1592
1082 static void reset_clip(redraw_man_t *rdman) { 1593 static void reset_clip(canvas_t *cr) {
1083 } 1594 }
1084 1595
1085 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas, 1596 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
1086 area_t **dirty_areas) { 1597 area_t **dirty_areas) {
1087 } 1598 }
1088 #endif /* UNITTEST */ 1599 #endif /* UNITTEST */
1600
1601 static int is_area_in_areas(area_t *area,
1602 int n_areas,
1603 area_t **areas) {
1604 int i;
1605
1606 for(i = 0; i < n_areas; i++) {
1607 if(areas_are_overlay(area, areas[i]))
1608 return 1;
1609 }
1610 return 0;
1611 }
1089 1612
1090 static int is_geo_in_areas(geo_t *geo, 1613 static int is_geo_in_areas(geo_t *geo,
1091 int n_areas, 1614 int n_areas,
1092 area_t **areas) { 1615 area_t **areas) {
1093 int i; 1616 return is_area_in_areas(geo->cur_area, n_areas, areas);
1094 1617 }
1095 for(i = 0; i < n_areas; i++) { 1618
1096 if(areas_are_overlay(geo->cur_area, areas[i])) 1619 static void update_cached_canvas_2_parent(redraw_man_t *rdman,
1097 return 1; 1620 coord_t *coord) {
1098 }
1099 return 0;
1100 }
1101
1102 static void update_canvas_2_parent(redraw_man_t *rdman, coord_t *coord) {
1103 cairo_t *pcanvas, *canvas; 1621 cairo_t *pcanvas, *canvas;
1104 cairo_surface_t *surface; 1622 cairo_surface_t *surface;
1105 1623 cairo_pattern_t *pattern;
1106 if(coord == rdman->root_coord) 1624 cairo_matrix_t cr_matrix;
1625 co_aix reverse[6];
1626 co_aix canvas2pdev_matrix[6];
1627
1628 if(coord_is_root(coord))
1107 return; 1629 return;
1108 1630
1109 canvas = coord->canvas; 1631 compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
1110 pcanvas = coord->parent->canvas; 1632 compute_reverse(canvas2pdev_matrix, reverse);
1633
1634 cr_matrix.xx = reverse[0];
1635 cr_matrix.xy = reverse[1];
1636 cr_matrix.x0 = reverse[2];
1637 cr_matrix.yx = reverse[3];
1638 cr_matrix.yy = reverse[4];
1639 cr_matrix.y0 = reverse[5];
1640
1641 canvas = _coord_get_canvas(coord);
1642 pcanvas = _coord_get_canvas(coord->parent);
1111 surface = cairo_get_target(canvas); 1643 surface = cairo_get_target(canvas);
1112 cairo_set_source_surface(pcanvas, surface, 0, 0); 1644 pattern = cairo_pattern_create_for_surface(surface);
1645 cairo_pattern_set_matrix(pattern, &cr_matrix);
1646 cairo_set_source(pcanvas, pattern);
1113 cairo_paint_with_alpha(pcanvas, coord->opacity); 1647 cairo_paint_with_alpha(pcanvas, coord->opacity);
1114 } 1648 }
1115 1649
1116 static int draw_coord_shapes_in_areas(redraw_man_t *rdman, 1650 static int draw_coord_shapes_in_dirty_areas(redraw_man_t *rdman,
1117 coord_t *coord, 1651 coord_t *coord) {
1118 int n_areas,
1119 area_t **areas) {
1120 int dirty = 0; 1652 int dirty = 0;
1121 int r; 1653 int r;
1654 area_t **areas;
1655 int n_areas;
1656 cairo_t *canvas;
1122 geo_t *member; 1657 geo_t *member;
1123 coord_t *child; 1658 coord_t *child;
1124 cairo_t *canvas;
1125 int mem_idx; 1659 int mem_idx;
1126 1660
1127 if(coord->flags & COF_HIDDEN) 1661 if(coord->flags & COF_HIDDEN)
1128 return OK; 1662 return OK;
1129 1663
1130 canvas = coord->canvas; 1664 areas = _coord_get_dirty_areas(coord)->ds;
1665 n_areas = _coord_get_dirty_areas(coord)->num;
1666 canvas = _coord_get_canvas(coord);
1667
1131 member = FIRST_MEMBER(coord); 1668 member = FIRST_MEMBER(coord);
1132 mem_idx = 0; 1669 mem_idx = 0;
1133 child = FIRST_CHILD(coord); 1670 child = FIRST_CHILD(coord);
1134 while(child != NULL || member != NULL) { 1671 while(child != NULL || member != NULL) {
1135 if(child && child->before_pmem == mem_idx) { 1672 if(child && child->before_pmem == mem_idx) {
1136 r = draw_coord_shapes_in_areas(rdman, child, n_areas, areas); 1673 if(child->flags & COF_OWN_CANVAS) {
1137 dirty |= r; 1674 if(!(child->flags & COF_HIDDEN) &&
1675 is_area_in_areas(coord_get_area(child), n_areas, areas)) {
1676 update_cached_canvas_2_parent(rdman, child);
1677 dirty = 1;
1678 }
1679 } else {
1680 r = draw_coord_shapes_in_dirty_areas(rdman, child);
1681 dirty |= r;
1682 }
1138 child = NEXT_CHILD(child); 1683 child = NEXT_CHILD(child);
1139 } else { 1684 } else {
1140 ASSERT(member != NULL); 1685 ASSERT(member != NULL);
1141 if((!(member->flags & GEF_HIDDEN)) && 1686 if((!(member->flags & GEF_HIDDEN)) &&
1142 is_geo_in_areas(member, n_areas, areas)) { 1687 is_geo_in_areas(member, n_areas, areas)) {
1147 member = NEXT_MEMBER(member); 1692 member = NEXT_MEMBER(member);
1148 mem_idx++; 1693 mem_idx++;
1149 } 1694 }
1150 } 1695 }
1151 1696
1152 if(dirty && coord->flags & COF_OWN_CANVAS) {
1153 update_canvas_2_parent(rdman, coord);
1154 clean_canvas_black(coord->canvas, rdman->w, rdman->h);
1155 }
1156
1157 return dirty; 1697 return dirty;
1158 } 1698 }
1159 1699
1160 static void draw_shapes_in_areas(redraw_man_t *rdman, 1700 static int draw_dirty_cached_coord(redraw_man_t *rdman,
1161 int n_areas, 1701 coord_t *coord) {
1162 area_t **areas) { 1702 area_t **areas, *area;
1163 draw_coord_shapes_in_areas(rdman, rdman->root_coord, n_areas, areas); 1703 int n_areas;
1704 cairo_t *canvas;
1705 int i;
1706 int r;
1707
1708 areas = _coord_get_dirty_areas(coord)->ds;
1709 n_areas = _coord_get_dirty_areas(coord)->num;
1710
1711 for(i = 0; i < n_areas; i++) {
1712 area = areas[i];
1713 area->x = floorf(area->x);
1714 area->y = floorf(area->y);
1715 area->w = ceilf(area->w);
1716 area->h = ceilf(area->h);
1717 }
1718
1719 canvas = _coord_get_canvas(coord);
1720 make_clip(canvas, n_areas, areas);
1721 clear_canvas(canvas);
1722
1723 r = draw_coord_shapes_in_dirty_areas(rdman, coord);
1724
1725 reset_clip(canvas);
1726 }
1727
1728 static void draw_shapes_in_dirty_areas(redraw_man_t *rdman) {
1729 int i;
1730 coord_t *coord;
1731
1732 for(i = rdman->zeroing_coords.num - 1; i >= 0; i--) {
1733 coord = rdman->zeroing_coords.ds[i];
1734 draw_dirty_cached_coord(rdman, coord);
1735 }
1164 } 1736 }
1165 1737
1166 1738
1167 /*! \brief Re-draw all changed shapes or shapes affected by changed coords. 1739 /*! \brief Re-draw all changed shapes or shapes affected by changed coords.
1168 * 1740 *
1196 * corod objects. 1768 * corod objects.
1197 * 1769 *
1198 */ 1770 */
1199 int rdman_redraw_changed(redraw_man_t *rdman) { 1771 int rdman_redraw_changed(redraw_man_t *rdman) {
1200 int r; 1772 int r;
1201 int n_dirty_areas;
1202 area_t **dirty_areas;
1203 event_t event; 1773 event_t event;
1204 subject_t *redraw; 1774 subject_t *redraw;
1205 1775 int i;
1776 coord_t *coord, **coords;
1777 int n_areas;
1778 area_t **areas;
1779
1206 r = clean_rdman_dirties(rdman); 1780 r = clean_rdman_dirties(rdman);
1207 if(r != OK) 1781 if(r != OK)
1208 return ERR; 1782 return ERR;
1209 1783
1210 n_dirty_areas = rdman->dirty_areas.num; 1784 if(rdman->n_dirty_areas > 0) {
1211 dirty_areas = rdman->dirty_areas.ds;
1212 if(n_dirty_areas > 0) {
1213 /*! \brief Draw shapes in preorder of coord tree and support opacity 1785 /*! \brief Draw shapes in preorder of coord tree and support opacity
1214 * rules. 1786 * rules.
1215 */ 1787 */
1216 clean_canvas(rdman->cr, rdman->w, rdman->h); 1788 draw_shapes_in_dirty_areas(rdman);
1217 draw_shapes_in_areas(rdman, n_dirty_areas, dirty_areas); 1789 n_areas = _coord_get_dirty_areas(rdman->root_coord)->num;
1218 copy_cr_2_backend(rdman, rdman->dirty_areas.num, 1790 areas = _coord_get_dirty_areas(rdman->root_coord)->ds;
1219 rdman->dirty_areas.ds); 1791 copy_cr_2_backend(rdman, n_areas, areas);
1220 reset_clip(rdman); 1792 reset_clip(rdman->backend);
1221 } 1793 for(i = 0; i < rdman->zeroing_coords.num; i++) {
1222 rdman->dirty_areas.num = 0; 1794 coord = rdman->zeroing_coords.ds[i];
1223 1795 DARRAY_CLEAN(_coord_get_dirty_areas(coord));
1796 }
1797 rdman->n_dirty_areas = 0;
1798 }
1799
1800 coords = rdman->zeroing_coords.ds;
1801 for(i = 0; i < rdman->zeroing_coords.num; i++) {
1802 coord = coords[i];
1803 coord_clear_flags(coord, COF_MUST_ZEROING);
1804 }
1805
1806 DARRAY_CLEAN(&rdman->dirty_coords);
1807 DARRAY_CLEAN(&rdman->dirty_geos);
1808 DARRAY_CLEAN(&rdman->zeroing_coords);
1809
1224 /* Free postponsed removing */ 1810 /* Free postponsed removing */
1225 free_free_objs(rdman); 1811 free_free_objs(rdman);
1226 1812
1227 redraw = rdman_get_redraw_subject(rdman); 1813 redraw = rdman_get_redraw_subject(rdman);
1228 event.type = EVT_RDMAN_REDRAW; 1814 event.type = EVT_RDMAN_REDRAW;
1254 * 1840 *
1255 * \sa 1841 * \sa
1256 * - rdman_redraw_all() 1842 * - rdman_redraw_all()
1257 * - rdman_redraw_changed() 1843 * - rdman_redraw_changed()
1258 * - draw_shapes_in_areas() 1844 * - draw_shapes_in_areas()
1845 *
1846 * \section img_cache Image Cache
1847 * It costs time to redraw every component in a complete graphic.
1848 * Image cache try to cache result of prviously rendering, and reusing it
1849 * to avoid wasting CPU time on repeatitive and redundant rendering.
1850 *
1851 * \ref COF_FAST_CACHE and \ref COF_PRECISE_CACHE are used to tag a
1852 * coord that it's
1853 * rendering result is cached in fast way or precise way. With fast cache,
1854 * MB renders descendants of a coord in once, and reuse the result until it
1855 * being dirty. With precise cache, it alike fast cache, but it also
1856 * performs rendering when an ancester of the coord transform it to larger
1857 * size, in width or height.
1858 *
1859 * coord_t::aggr_matrix of a cached coord is computed from aggr_matrix of
1860 * parent. But, it does not use one from parent directly. parent one is
1861 * transformed as
1862 * \code
1863 * cache_scale_x = sqrt(p_matrix[0]**2 + p_matrix[3]**2);
1864 * cache_scale_y = sqrt(p_matrix[1]**2 + p_matrix[4]**2);
1865 * cache_p_matrix[0] = cache_scale_x;
1866 * cache_p_matrix[1] = 0;
1867 * cache_p_matrix[2] = range_shift_x;
1868 * cache_p_matrix[3] = 0;
1869 * cache_p_matrix[4] = cache_scale_y;
1870 * cache_p_matrix[5] = range_shift_y;
1871 * \endcode
1872 * where p_matrix is parent one, and cache_p_matrix is one derived from
1873 * parent one. coord_t::aggr_matrix of a cached coord is
1874 * \code
1875 * aggr_matrix = cache_p_matrix * matrix
1876 * \endcode
1877 * where matrix is the transform being installed on the cached coord.
1878 * range_shift_x and range_shift_y are defined above.
1879 *
1880 * cache_p_matrix rescales sub-graphic to an appropriately size
1881 * (cache_scale_x, cache_scale_y) and aligns left-top of the minimum
1882 * rectangle (range_shift_x, range_shift_y) that cover the area occupied
1883 * by sub-graphic with origin of the space.
1884 *
1885 * The sub-graphic should be rendered on space defined by cache_p_matrix of
1886 * cached one. But rendering result are transformed to the space defined
1887 * by parent with following matrix.
1888 * \code
1889 * draw_matrix = reverse(p_matrix * reverse(cache_p_matrix))
1890 * \endcode
1891 * With Cairo, draw_matrix is applied on source surface (canvas)
1892 * to draw image to parent's surface (canvas). draw_matrix is a function
1893 * map points from parent space to the space of cached one.
1894 *
1895 * Cached coords are marked for changing transformation of ancestors only if
1896 * following condition is true.
1897 * \code
1898 * cache_scale_x < sqrt(p_matrix[0]**2 + p_matrix[3]**2) ||
1899 * cache_scale_y < sqrt(p_matrix[1]**2 + p_matrix[4]**2)
1900 * \endcode
1901 * where p_matrix is latest aggr_matrix of parent after changing
1902 * transformation, and where cache_scale_* are ones mention above and computed
1903 * before changing transformation of ancestors.
1904 *
1905 * Cache_scale_* can be recovered by following instructions.
1906 * \code
1907 * cache_scale_x = aggr_matrix[0] / matrix[0];
1908 * cache_scale_y = aggr_matrix[4] / matrix[4];
1909 * \endcode
1910 *
1911 * \section cache_area Area of cached coord
1912 * - *_transform of shapes works as normal
1913 * - areas of descendants of cached coord are in space defined
1914 * by aggr_matrix of cached coord.
1915 * - descendants are marked with \ref COF_ANCESTOR_CACHE
1916 *
1917 * Since *_transform of shapes compute area with aggr_matrix that is
1918 * derived from aggr_matrix of a cached ancestor, area of
1919 * \ref COF_ANCESTOR_CACHE ones should be transformed to device space in
1920 * find_shape_at_pos() with following statement.
1921 * \code
1922 * area_matrix = p_matrix * reverse(cache_p_matrix)
1923 * \endcode
1924 * where cache_p_matrix and p_matrix are corresponding matrix of
1925 * cached ancestor. We can also perform transforming in reversed
1926 * direction to transform point to space defined by aggr_matrix of cached
1927 * coord.
1928 *
1929 * Since it is costly to transform area of \ref COF_ANCESTOR_CACHE ones to
1930 * device space if more than one ancestor are cached, no ancestor of
1931 * cached coord can be set to cached.
1932 *
1933 * \section cached_bounding Bounding box of cached coord and descendants
1934 * Bounding box of a cached coord and it's descendants is the range that
1935 * cached coord and descendants are rendered on canvas. It is also called
1936 * cached-bounding.
1937 *
1938 * range_shift_x and range_shift_y are computed by initailizing cache_p_matrix
1939 * with range_shift_x == range_shift_y == 0 at first. cache_p_matrix is
1940 * used to compute aggr_matrix and cached-bounding in turn. Then,
1941 * range_shift_x and range_shift_y are initialized to negative of
1942 * x-axis and y-axis, repectively, of left-top of cached-bounding. Then,
1943 * aggr_matrix of cached coord and descendants are updated by
1944 * following statements.
1945 * \code
1946 * aggr_matrix[2] += range_shift_x;
1947 * aggr_matrix[5] += range_shift_y;
1948 * \endcode
1949 * The statements shift the spaces to make cached-bounding
1950 * aligned to origin of coordinate system.
1951 * The purpose of range_shift_* is to reduce size of canvas used to cache
1952 * rendering result. The canvas are shrink to size the same as bounding
1953 * box.
1954 *
1955 * \section cache_redraw How cache and redraw work together?
1956 * When a coord and descedants are cached, the coord is flaged with
1957 * COF_FAST_CACHE or COF_PRECISE_CACHE. When a coord is marked dirty, all
1958 * descendants are also marked dirty by rdman except descendants of cached
1959 * ones. But, cached ones are also marked dirty as normal ones. The
1960 * reason to mark cached ones is giving them a chance to update their
1961 * area.
1962 *
1963 * For precise cached descendants, above rule has an exception. They should
1964 * also be marked dirty if cached coord should be rendered in a larger
1965 * resize factor to get better output.
1966 *
1967 * coord_t::aggr_matrix and cached-bounding of cached coord must be computed
1968 * in the way described in \ref cached_bounding. Propagating range_shift_*
1969 * to descendants must skip cached ones and their descendants.
1970 * Range_shift_* are computed after updating descendants. So, procedure
1971 * of clean descendants of a cached one must performed in two phases.
1972 * One for computing areas of descendants and one for propagating
1973 * range_shift_*.
1974 *
1975 * A cached coord or/and descendants are dirty only for cached coord or
1976 * descendants being marked dirty by application. Once a cached coord or
1977 * descendant is marked dirty, all descendants of marked one are also
1978 * marked. redraw_man_t::dirty_areas collects areas, in device space,
1979 * that should be updated. All shapes overlaid with any area in
1980 * redraw_man_t::dirty_areas should be redraw. Since descendants of cached
1981 * coord compute their areas in spaces other than device space.
1982 * Separated lists should be maintained for each cached coord and it's
1983 * descendants.
1984 *
1985 * \section cache_imp Implementation of Cache
1986 * Both cached coords and coords that opacity != 1 need a canvas to
1987 * draw descendants on. Both cases are traded in the same way.
1988 * Every of them own a canvas_info to describe canvas and related
1989 * information. aggr_matrix of descendants must be adjusted to make
1990 * left-top of range just at origin of canvas. It can save space by setting
1991 * just large enough to hold rendering result of descendants. The process
1992 * of adjusting is zeroing.
1993 *
1994 * Following is rules.
1995 * - zeroing on a cached coord is performed by adjust coord_t::aggr_matrix
1996 * of the cached coord and descendnats.
1997 * - Clean coords works just like before without change.
1998 * - in preorder
1999 * - never perform zeroing on root_coord.
2000 * - zeroing on cached coords marked with \ref COF_MUST_ZEROING.
2001 * - when clean a descendant that moves out-side of it's canvas,
2002 * respective cached coord is marked with \ref COF_MUST_ZEROING.
2003 * - zeroing is performed immediately after clean coords.
2004 * - zeroing will not propagate acrossing boundary of cached coord.
2005 * - It will be stopped at descendants which are cached coords.
2006 * - coord_t::cur_area and coord_t::aggr_matrix of cached coords
2007 * must be ajdusted.
2008 * - the area of a cached coord is defined in parent space.
2009 * - areas of descendants are defined in space defined by aggr_matrix of
2010 * cached coord.
2011 * - parent know the area in where cached coord and descendnats will
2012 * be draw.
2013 * - cached coords keep their private dirty area list.
2014 * - private dirty areas of a cached coord are transformed and added to
2015 * parent cached coord.
2016 * - aggregates areas before adding to parent.
2017 * - canvas of a cached coord is updated if
2018 * - descendants are dirty, or
2019 * - it-self is dirty.
2020 * - change of a canvas must copy to canvas of parent space.
2021 * - a cached is updated if canvas of descendant cached coord is updated.
2022 * - updating canvas is performed by redraw dirty areas.
2023 * - since dirty areas of cached ones would be aggregated and added to
2024 * parent, parent cached coord would copy it from cache of descedants.
2025 * - descendant cached coords must be updated before ancestor cached coords.
2026 * - add dirty areas to parent immediately after updating canvas.
2027 * - Making dirty coords is not propagated through cached ones.
2028 * - cached ones are also made dirty, but stop after that.
2029 *
2030 * Steps:
2031 * - SWAP coord_t::cur_area of dirty coords.
2032 * - SWAP geo_t::cur_area of dirty geos.
2033 * - clean coords
2034 * - coord_t::aggr_matrix of cached coord is not the same as non-cached.
2035 * - see \ref img_cache
2036 * - clean geos
2037 * - Add canvas owner of dirty geos to redraw_man_t::zeroing_coords
2038 * - Cached ancestors of redraw_man_t::dirty_geos
2039 * - Cached ancestors of redraw_man_t::dirty_coords
2040 * - Cached ancestors of zeroed ones should also be zeroed.
2041 * - zeroing
2042 * - Add more dirty areas if canvas should be fully redrawed.
2043 * - From leaf to root.
2044 * - add aggregated dirty areas from descendant cached coords to ancestors.
2045 * - Must include old area of cached coords if it is just clean and
2046 * parent cached one is not just clean.
2047 * - Just clean is a coord cleaned in last time of cleaning coords.
2048 * - draw dirty areas
2049 * - areas are rounded to N at first.
2050 * - from leaf to root.
1259 */ 2051 */
1260 2052
1261 int rdman_redraw_all(redraw_man_t *rdman) { 2053 int rdman_redraw_all(redraw_man_t *rdman) {
1262 area_t area; 2054 area_t area;
1263 #ifndef UNITTEST 2055 #ifndef UNITTEST
1272 area.h = cairo_image_surface_get_height(surface); 2064 area.h = cairo_image_surface_get_height(surface);
1273 #else 2065 #else
1274 area.w = 1024; 2066 area.w = 1024;
1275 area.h = 1024; 2067 area.h = 1024;
1276 #endif 2068 #endif
1277 add_dirty_area(rdman, &area); 2069 add_dirty_area(rdman, rdman->root_coord, &area);
1278 2070
1279 r = rdman_redraw_changed(rdman); 2071 r = rdman_redraw_changed(rdman);
1280 if(r != OK) 2072 if(r != OK)
1281 return ERR; 2073 return ERR;
1282 2074
1290 2082
1291 area.x = x; 2083 area.x = x;
1292 area.y = y; 2084 area.y = y;
1293 area.w = w; 2085 area.w = w;
1294 area.h = h; 2086 area.h = h;
1295 add_dirty_area(rdman, &area); 2087 add_dirty_area(rdman, rdman->root_coord, &area);
1296 2088
1297 r = rdman_redraw_changed(rdman); 2089 r = rdman_redraw_changed(rdman);
1298 2090
1299 return r; 2091 return r;
1300 } 2092 }
1301 2093
2094 /*! \brief Helping function to travel descendant shapes of a coord.
2095 */
1302 geo_t *rdman_geos(redraw_man_t *rdman, geo_t *last) { 2096 geo_t *rdman_geos(redraw_man_t *rdman, geo_t *last) {
1303 geo_t *next; 2097 geo_t *next;
1304 coord_t *coord; 2098 coord_t *coord;
1305 2099
1306 if(last == NULL) { 2100 if(last == NULL) {