comparison src/redraw_man.c @ 327:85b8bb36fe71

Move and update in source documents
author Thinker K.F. Li <thinker@branda.to>
date Fri, 06 Mar 2009 13:53:27 +0800
parents c1afd14caa85
children 3e84458968ec
comparison
equal deleted inserted replaced
326:85951268ee0f 327:85b8bb36fe71
8 #include "mb_tools.h" 8 #include "mb_tools.h"
9 #include "mb_redraw_man.h" 9 #include "mb_redraw_man.h"
10 #include "mb_observer.h" 10 #include "mb_observer.h"
11 #include "mb_prop.h" 11 #include "mb_prop.h"
12 12
13 #ifndef ASSERT 13 /*! \page dirty Dirty geo, coord, and area.
14 #define ASSERT(x) 14 *
15 #endif 15 * \section dirty_of_ego Dirty of geo
16 16 * A geo is dirty when any of the shape, size or positions is changed.
17 /* NOTE: bounding box should also consider width of stroke. 17 * It's geo and positions should be recomputed before drawing. So,
18 */ 18 * dirty geos are marked as dirty and put into redraw_man_t::dirty_geos list.
19 19 * geos in the list are cleaned to compute information as a reaction for
20 #define sh_attach_geo(sh, g) \ 20 * dirty. It recomputes size, position and other data of
21 do { \ 21 * repective shapes.
22 (sh)->geo = g; \ 22 *
23 (g)->shape = (shape_t *)(sh); \ 23 * \section dirty_of_coord Dirty of coord
24 } while(0) 24 * A coord is dirty when it's transformation matrix being changed.
25 #define sh_detach_geo(sh) \ 25 * Dirty coords are marked as dirty and put into dirty_coords list.
26 do { \ 26 * Once a coord is dirty, every member geos of it are also dirty.
27 (sh)->geo->shape = NULL; \ 27 * Because, their shape, size and positions will be changed. But,
28 (sh)->geo = NULL; \ 28 * they are not marked as dirty and put into dirty_geos list, since
29 } while(0) 29 * all these member geos will be recomputed for computing new current
30 #define sh_get_geo(sh) ((sh)->geo) 30 * area of the coord. The changes of a coord also affect child
31 #define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0) 31 * coords. Once parent is dirty, all children are also dirty for
32 #define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0) 32 * their aggregate matrix out of date. Dirty coords should be
33 #define rdman_is_dirty(rdman) \ 33 * clean in preorder of tree traversal. The redraw_man_t::dirty_coords
34 ((rdman)->dirty_coords.num != 0 || \ 34 * list are sorted to keep ordering before cleaning.
35 (rdman)->dirty_geos.num != 0) 35 * Whenever a coord is marked dirty and put into redraw_man_t::dirty_coords
36 36 * list, all it's children should also be marked.
37 #define OK 0 37 *
38 #define ERR -1 38 * The procedure of clean coords comprises recomputing aggregate
39 39 * transform matrix and area where members spreading in. The aggregated
40 #define ARRAY_EXT_SZ 64 40 * transform matrix can reduce number of matrix mul to transform
41 41 * positions from space of a coord to the closest cached ancestor coord.
42 #define SWAP(a, b, t) do { t c; c = a; a = b; b = c; } while(0) 42 *
43 43 * The list is inspected before drawing to recompute new shape, size,
44 #ifdef UNITTEST 44 * and positions of member geos of coords in the list. The drity
45 typedef struct _sh_dummy sh_dummy_t; 45 * flag of member geos will be clean.
46 46 *
47 extern void sh_dummy_transform(shape_t *shape); 47 * Clean coords should be performed before clean geos, since clean
48 extern void sh_dummy_fill(shape_t *, cairo_t *); 48 * coords will also clean member geos.
49 #endif /* UNITTEST */ 49 *
50 50 * \section dirty_of_area Dirty of area
51 static subject_t *ob_subject_alloc(ob_factory_t *factory); 51 * When an area is dirty, it is added to coord_canvas_info_t::dirty_areas
52 static void ob_subject_free(ob_factory_t *factory, subject_t *subject); 52 * of it's closest cached coord. Areas are created when a shape is cleaned
53 static observer_t *ob_observer_alloc(ob_factory_t *factory); 53 * for dirty. The areas where a cleaned shape occupied before and after
54 static void ob_observer_free(ob_factory_t *factory, observer_t *observer); 54 * cleaning should be redrawed. Areas are added to dirty area list to
55 static subject_t *ob_get_parent_subject(ob_factory_t *factory, 55 * mark areas where should be redrawed. So, all shapes covered by
56 subject_t *cur_subject); 56 * dirty area list should be redrawed to update these areas. So, areas
57 /* Functions for children. */ 57 * are added to dirty lists after cleaning geos due to changes of
58 #define FORCHILDREN(coord, child) \ 58 * shapes.
59 for((child) = STAILQ_HEAD((coord)->children); \ 59 *
60 (child) != NULL; \ 60 * For example, when a shape is moved from location A to location B,
61 (child) = STAILQ_NEXT(coord_t, sibling, (child))) 61 * areas where the shape occupied for A and B are changed for moving.
62 #define NEXT_CHILD(child) STAILQ_NEXT(coord_t, sibling, child) 62 * Bothe areas are added into dirty list to mark these areas should
63 #define ADD_CHILD(parent, child) \ 63 * be redrawed.
64 STAILQ_INS_TAIL((parent)->children, coord_t, sibling, (child))
65 #define RM_CHILD(parent, child) \
66 STAILQ_REMOVE((parent)->children, coord_t, sibling, (child))
67 #define FIRST_CHILD(parent) STAILQ_HEAD((parent)->children)
68
69 /* Functions for members. */
70 #define FORMEMBERS(coord, member) \
71 for((member) = STAILQ_HEAD((coord)->members); \
72 (member) != NULL; \
73 (member) = STAILQ_NEXT(geo_t, coord_next, (member)))
74 #define NEXT_MEMBER(member) STAILQ_NEXT(geo_t, coord_next, (member))
75 #define ADD_MEMBER(coord, member) \
76 STAILQ_INS_TAIL((coord)->members, geo_t, coord_next, (member))
77 #define RM_MEMBER(coord, member) \
78 STAILQ_REMOVE((coord)->members, geo_t, coord_next, (member))
79 #define FIRST_MEMBER(coord) STAILQ_HEAD((coord)->members)
80
81 /* Functions for paint members. */
82 #define FORPAINTMEMBERS(paint, member) \
83 for((member) = STAILQ_HEAD((paint)->members); \
84 (member) != NULL; \
85 (member) = STAILQ_NEXT(paint_t, next, member))
86 #define RM_PAINTMEMBER(paint, member) \
87 STAILQ_REMOVE((paint)->members, shnode_t, next, member)
88 #define RM_PAINT(rdman, paint) \
89 STAILQ_REMOVE((rdman)->paints, paint_t, pnt_next, paint)
90
91 /*! \brief Sort a list of element by a unsigned integer.
92 *
93 * The result is in ascend order. The unsigned integers is
94 * at offset specified by 'off' from start address of elemnts.
95 */
96 static void _insert_sort(void **elms, int num, int off) {
97 int i, j;
98 unsigned int val;
99 void *elm_i;
100
101 for(i = 1; i < num; i++) {
102 elm_i = elms[i];
103 val = *(unsigned int *)(elm_i + off);
104 for(j = i; j > 0; j--) {
105 if(*(unsigned int *)(elms[j - 1] + off) <= val)
106 break;
107 elms[j] = elms[j - 1];
108 }
109 elms[j] = elm_i;
110 }
111 }
112
113 DARRAY_DEFINE(coords, coord_t *);
114 DARRAY_DEFINE(geos, geo_t *);
115 DARRAY_DEFINE(areas, area_t *);
116
117 int rdman_add_gen_geos(redraw_man_t *rdman, geo_t *geo) {
118 int r;
119
120 r = geos_add(rdman_get_gen_geos(rdman), geo);
121 return r;
122 }
123
124 /*! Use \brief DARRAY to implement dirty & free lists.
125 */
126 #define ADD_DATA(sttype, field, v) \
127 int r; \
128 r = sttype ## _add(&rdman->field, v); \
129 return r == 0? OK: ERR;
130
131
132 static int add_dirty_coord(redraw_man_t *rdman, coord_t *coord) {
133 coord->flags |= COF_DIRTY;
134 ADD_DATA(coords, dirty_coords, coord);
135 return OK;
136 }
137
138 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
139 geo->flags |= GEF_DIRTY;
140 ADD_DATA(geos, dirty_geos, geo);
141 return OK;
142 }
143
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;
156 }
157
158 static int add_free_obj(redraw_man_t *rdman, void *obj,
159 free_func_t free_func) {
160 int max;
161 free_obj_t *new_objs, *free_obj;
162
163 if(rdman->free_objs.num >= rdman->free_objs.max) {
164 max = rdman->free_objs.num + ARRAY_EXT_SZ;
165 new_objs = realloc(rdman->free_objs.objs,
166 max * sizeof(free_obj_t));
167 if(new_objs == NULL)
168 return ERR;
169 rdman->free_objs.max = max;
170 rdman->free_objs.objs = new_objs;
171 }
172
173 free_obj = rdman->free_objs.objs + rdman->free_objs.num++;
174 free_obj->obj = obj;
175 free_obj->free_func = free_func;
176
177 return OK;
178 }
179
180 static void free_free_objs(redraw_man_t *rdman) {
181 int i;
182 free_obj_t *free_obj;
183
184 for(i = 0; i < rdman->free_objs.num; i++) {
185 free_obj = &rdman->free_objs.objs[i];
186 free_obj->free_func(rdman, free_obj->obj);
187 }
188 rdman->free_objs.num = 0;
189 }
190
191 static void free_objs_destroy(redraw_man_t *rdman) {
192 if(rdman->free_objs.objs != NULL)
193 free(rdman->free_objs.objs);
194 }
195
196 static void area_to_positions(area_t *area, co_aix (*poses)[2]) {
197 poses[0][0] = area->x;
198 poses[0][1] = area->y;
199 poses[1][0] = area->x + area->w;
200 poses[1][1] = area->y + area->h;;
201 }
202
203 static cairo_t *canvas_new(int w, int h) {
204 #ifndef UNITTEST
205 cairo_surface_t *surface;
206 cairo_t *cr;
207
208 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
209 w, h);
210 cr = cairo_create(surface);
211
212 return cr;
213 #else
214 return NULL;
215 #endif
216 }
217
218 static void canvas_free(cairo_t *canvas) {
219 #ifndef UNITTEST
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;
234 #endif
235 }
236
237 static int geo_off_in_coord(geo_t *geo, coord_t *coord) {
238 int off = 0;
239 geo_t *vgeo;
240
241 FORMEMBERS(coord, vgeo) {
242 if(vgeo == geo)
243 return off;
244 off++;
245 }
246 return -1;
247 }
248
249 static void geo_attach_coord(geo_t *geo, coord_t *coord) {
250 ADD_MEMBER(coord, geo);
251 coord->num_members++;
252 }
253
254 static void geo_detach_coord(geo_t *geo, coord_t *coord) {
255 int off;
256 coord_t *child;
257
258 off = geo_off_in_coord(geo, coord);
259 if(off < 0)
260 return;
261 FORCHILDREN(coord, child) {
262 if(child->before_pmem >= off)
263 child->before_pmem--;
264 }
265
266 RM_MEMBER(coord, geo);
267 coord->num_members--;
268 }
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
292 static void mouse_event_root_dummy(event_t *evt, void *arg) {
293 }
294
295 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr, cairo_t *backend) {
296 extern void redraw_man_destroy(redraw_man_t *rdman);
297 extern int _paint_color_size;
298 observer_t *addrm_ob;
299 extern void addrm_monitor_hdlr(event_t *evt, void *arg);
300
301 memset(rdman, 0, sizeof(redraw_man_t));
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
308 rdman->geo_pool = elmpool_new(sizeof(geo_t), 128);
309 rdman->coord_pool = elmpool_new(sizeof(coord_t), 16);
310 rdman->shnode_pool = elmpool_new(sizeof(shnode_t), 16);
311 rdman->observer_pool = elmpool_new(sizeof(observer_t), 32);
312 rdman->subject_pool = elmpool_new(sizeof(subject_t), 32);
313 rdman->paint_color_pool = elmpool_new(_paint_color_size, 64);
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);
316 if(!(rdman->geo_pool && rdman->coord_pool && rdman->shnode_pool &&
317 rdman->observer_pool && rdman->subject_pool &&
318 rdman->paint_color_pool && rdman->coord_canvas_pool))
319 goto err;
320
321 rdman->ob_factory.subject_alloc = ob_subject_alloc;
322 rdman->ob_factory.subject_free = ob_subject_free;
323 rdman->ob_factory.observer_alloc = ob_observer_alloc;
324 rdman->ob_factory.observer_free = ob_observer_free;
325 rdman->ob_factory.get_parent_subject = ob_get_parent_subject;
326
327 rdman->redraw =
328 subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
329 rdman->addrm_monitor =
330 subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
331 if(!(rdman->redraw && rdman->addrm_monitor))
332 goto err;
333
334 addrm_ob = subject_add_observer(rdman->addrm_monitor,
335 addrm_monitor_hdlr, rdman);
336 if(addrm_ob == NULL)
337 goto err;
338
339 rdman->last_mouse_over = NULL;
340
341 rdman->root_coord = elmpool_elm_alloc(rdman->coord_pool);
342 if(rdman->root_coord == NULL)
343 redraw_man_destroy(rdman);
344 rdman->n_coords = 1;
345 coord_init(rdman->root_coord, NULL);
346 mb_prop_store_init(&rdman->root_coord->obj.props, rdman->pent_pool);
347 rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory,
348 rdman->root_coord,
349 OBJT_COORD);
350 rdman->root_coord->flags |= COF_OWN_CANVAS;
351 rdman->root_coord->canvas_info =
352 coord_canvas_info_new(rdman, rdman->root_coord, cr);
353 rdman->root_coord->opacity = 1;
354
355 rdman->cr = cr;
356 rdman->backend = backend;
357
358 STAILQ_INIT(rdman->shapes);
359 STAILQ_INIT(rdman->paints);
360
361 /* \note To make root coord always have at leat one observer.
362 * It triggers mouse interpreter to be installed on root.
363 */
364 subject_set_monitor(rdman->root_coord->mouse_event,
365 rdman->addrm_monitor);
366 subject_add_observer(rdman->root_coord->mouse_event,
367 mouse_event_root_dummy, NULL);
368
369 mb_prop_store_init(&rdman->props, rdman->pent_pool);
370 return OK;
371
372 err:
373 if(rdman->geo_pool)
374 elmpool_free(rdman->geo_pool);
375 if(rdman->coord_pool)
376 elmpool_free(rdman->coord_pool);
377 if(rdman->shnode_pool)
378 elmpool_free(rdman->shnode_pool);
379 if(rdman->observer_pool)
380 elmpool_free(rdman->observer_pool);
381 if(rdman->subject_pool)
382 elmpool_free(rdman->subject_pool);
383 if(rdman->paint_color_pool)
384 elmpool_free(rdman->paint_color_pool);
385 if(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);
393 return ERR;
394 }
395
396 void redraw_man_destroy(redraw_man_t *rdman) {
397 coord_t *coord, *saved_coord;
398 shape_t *shape, *saved_shape;
399 geo_t *member;
400
401 mb_prop_store_destroy(&rdman->props);
402
403 free_free_objs(rdman);
404 free_objs_destroy(rdman);
405
406 coord = postorder_coord_subtree(rdman->root_coord, NULL);
407 while(coord) {
408 saved_coord = coord;
409 coord = postorder_coord_subtree(rdman->root_coord, coord);
410 FORMEMBERS(saved_coord, member) {
411 rdman_shape_free(rdman, member->shape);
412 }
413 rdman_coord_free(rdman, saved_coord);
414 }
415 #if 0
416 FORMEMBERS(saved_coord, member) {
417 rdman_shape_free(rdman, member->shape);
418 }
419 #endif
420 /* Resources of root_coord is free by elmpool_free() or
421 * caller; for canvas
422 */
423
424 shape = saved_shape = STAILQ_HEAD(rdman->shapes);
425 while(shape && (shape = STAILQ_NEXT(shape_t, sh_next, shape))) {
426 rdman_shape_free(rdman, saved_shape);
427 #if 0
428 STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, saved_shape);
429 #endif
430 saved_shape = shape;
431 }
432 if(saved_shape != NULL)
433 rdman_shape_free(rdman, saved_shape);
434
435 coord_canvas_info_free(rdman, rdman->root_coord->canvas_info);
436
437 elmpool_free(rdman->coord_pool);
438 elmpool_free(rdman->geo_pool);
439 elmpool_free(rdman->shnode_pool);
440 elmpool_free(rdman->observer_pool);
441 elmpool_free(rdman->subject_pool);
442 elmpool_free(rdman->paint_color_pool);
443 elmpool_free(rdman->pent_pool);
444 elmpool_free(rdman->coord_canvas_pool);
445
446 DARRAY_DESTROY(&rdman->dirty_coords);
447 DARRAY_DESTROY(&rdman->dirty_geos);
448 DARRAY_DESTROY(&rdman->gen_geos);
449 DARRAY_DESTROY(&rdman->zeroing_coords);
450 }
451
452
453 #define ASSERT(x)
454 /*
455 * Change transformation matrix
456 * - update aggregated transformation matrix
457 * - of coord_t object been changed.
458 * - of children coord_t objects.
459 * - redraw members of coord_t objects.
460 * - redraw shape objects they are overlaid with members.
461 * - find out overlaid shape objects.
462 * - geo_t of a coord_t object
463 * - can make finding more efficiency.
464 * - fill overlay geo_t objects of members.
465 *
466 * Change a shape object
467 * - redraw changed object.
468 * - redraw shape objects they are overlaid with changed object.
469 * - find out overlaid shape objects.
470 *
471 * That coord and geo of shape objects are setted by user code
472 * give user code a chance to collect coord and geo objects together
473 * and gain interest of higher cache hit rate.
474 */
475
476 int rdman_add_shape(redraw_man_t *rdman, shape_t *shape, coord_t *coord) {
477 geo_t *geo;
478 int r;
479
480 geo = elmpool_elm_alloc(rdman->geo_pool);
481 if(geo == NULL)
482 return ERR;
483
484 geo_init(geo);
485 geo->mouse_event = subject_new(&rdman->ob_factory, geo, OBJT_GEO);
486 subject_set_monitor(geo->mouse_event, rdman->addrm_monitor);
487
488 geo_attach_coord(geo, coord);
489
490 /* New one should be dirty to recompute it when drawing. */
491 r = add_dirty_geo(rdman, geo);
492 if(r != OK)
493 return ERR;
494
495 sh_attach_coord(shape, coord);
496 sh_attach_geo(shape, geo);
497
498 return OK;
499 }
500
501 /*! \brief Remove a shape object from redraw manager.
502 *
503 * \note Shapes should be removed after redrawing or when rdman is in clean.
504 * \note Removing shapes or coords when a rdman is dirty, removing
505 * is postponsed.
506 * \todo redraw shape objects that overlaid with removed one.
507 */
508 int rdman_shape_free(redraw_man_t *rdman, shape_t *shape) {
509 geo_t *geo;
510 int r;
511
512 geo = shape->geo;
513
514 if(rdman_is_dirty(rdman) && geo != NULL) {
515 if(geo->flags & GEF_FREE)
516 return ERR;
517
518 geo->flags |= GEF_FREE;
519 sh_hide(shape);
520 if(!(geo->flags & GEF_DIRTY)) {
521 r = add_dirty_geo(rdman, geo);
522 if(r != OK)
523 return ERR;
524 }
525 r = add_free_obj(rdman, shape, (free_func_t)rdman_shape_free);
526 if(r != OK)
527 return ERR;
528 return OK;
529 }
530
531 if(geo != NULL) {
532 subject_free(geo->mouse_event);
533 geo_detach_coord(geo, shape->coord);
534 sh_detach_coord(shape);
535 sh_detach_geo(shape);
536 elmpool_elm_free(rdman->geo_pool, geo);
537 }
538 STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, shape);
539 mb_prop_store_destroy(&shape->obj.props);
540 shape->free(shape);
541
542 if(rdman->last_mouse_over == (mb_obj_t *)shape)
543 rdman->last_mouse_over = NULL;
544
545 return OK;
546 }
547
548 shnode_t *shnode_new(redraw_man_t *rdman, shape_t *shape) {
549 shnode_t *node;
550
551 node = (shnode_t *)elmpool_elm_alloc(rdman->shnode_pool);
552 if(node) {
553 node->shape = shape;
554 node->next = NULL;
555 }
556 return node;
557 }
558
559 int rdman_paint_free(redraw_man_t *rdman, paint_t *paint) {
560 shnode_t *shnode, *saved_shnode;
561
562 if(rdman_is_dirty(rdman)) {
563 if(!(paint->flags & PNTF_FREE))
564 return ERR;
565 add_free_obj(rdman, paint, (free_func_t)rdman_paint_free);
566 paint->flags |= PNTF_FREE;
567 return OK;
568 }
569
570 /* Free member shapes that using this paint. */
571 saved_shnode = NULL;
572 FORPAINTMEMBERS(paint, shnode) {
573 if(saved_shnode) {
574 RM_PAINTMEMBER(paint, saved_shnode);
575 shnode_free(rdman, saved_shnode);
576 }
577 saved_shnode = shnode;
578 }
579 if(saved_shnode) {
580 RM_PAINTMEMBER(paint, saved_shnode);
581 shnode_free(rdman, saved_shnode);
582 }
583
584 RM_PAINT(rdman, paint);
585 paint->free(rdman, paint);
586 return OK;
587 }
588
589 void _rdman_paint_real_remove_child(redraw_man_t *rdman,
590 paint_t *paint,
591 shape_t *shape) {
592 shnode_t *shnode;
593
594 FORPAINTMEMBERS(paint, shnode) {
595 if(shnode->shape == shape) {
596 RM_PAINTMEMBER(paint, shnode);
597 shnode_free(rdman, shnode);
598 break;
599 }
600 }
601 }
602
603 coord_t *rdman_coord_new(redraw_man_t *rdman, coord_t *parent) {
604 coord_t *coord, *root_coord;
605 coord_t *visit;
606
607 coord = elmpool_elm_alloc(rdman->coord_pool);
608 if(coord == NULL)
609 return NULL;
610
611 coord_init(coord, parent);
612 mb_prop_store_init(&coord->obj.props, rdman->pent_pool);
613 coord->mouse_event = subject_new(&rdman->ob_factory,
614 coord,
615 OBJT_COORD);
616 subject_set_monitor(coord->mouse_event, rdman->addrm_monitor);
617 /*! \note default opacity == 1 */
618 coord->opacity = 1;
619 if(parent)
620 coord->canvas_info = parent->canvas_info;
621 rdman->n_coords++;
622
623 coord->order = ++rdman->next_coord_order;
624 if(coord->order == 0) {
625 rdman->next_coord_order = 0;
626 root_coord = visit = rdman->root_coord;
627 /* skip root coord. */
628 visit = preorder_coord_subtree(root_coord, visit);
629 while(visit) {
630 visit->order = ++rdman->next_coord_order;
631 visit = preorder_coord_subtree(root_coord, visit);
632 }
633 }
634
635 coord->before_pmem = parent->num_members;
636
637 /* If parent is dirty, children should be dirty. */
638 if(parent && (parent->flags & COF_DIRTY))
639 add_dirty_coord(rdman, coord);
640
641 return coord;
642 }
643
644 static int rdman_coord_free_postponse(redraw_man_t *rdman, coord_t *coord) {
645 int r;
646
647 if(coord->flags & COF_FREE)
648 return ERR;
649
650 coord->flags |= COF_FREE;
651 coord_hide(coord);
652 if(!(coord->flags & COF_DIRTY)) {
653 r = add_dirty_coord(rdman, coord);
654 if(r != OK)
655 return ERR;
656 }
657 r = add_free_obj(rdman, coord, (free_func_t)rdman_coord_free);
658 if(r != OK)
659 return ERR;
660 return OK;
661 }
662
663 /*! \brief Free a coord of a redraw_man_t object.
664 *
665 * All children and members should be freed before parent being freed.
666 *
667 * \param coord is a coord_t without children and members.
668 * \return 0 for successful, -1 for error.
669 *
670 * \note Free is postponsed if the coord is dirty or it has children
671 * or members postponsed for free.
672 */
673 int rdman_coord_free(redraw_man_t *rdman, coord_t *coord) {
674 coord_t *parent;
675 coord_t *child;
676 geo_t *member;
677 int cm_cnt; /* children & members counter */
678
679 parent = coord->parent;
680 if(parent == NULL)
681 return ERR;
682
683 cm_cnt = 0;
684 FORCHILDREN(coord, child) {
685 cm_cnt++;
686 if(!(child->flags & COF_FREE))
687 return ERR;
688 }
689 FORMEMBERS(coord, member) {
690 cm_cnt++;
691 if(!(member->flags & GEF_FREE))
692 return ERR;
693 }
694
695 if(cm_cnt || rdman_is_dirty(rdman))
696 return rdman_coord_free_postponse(rdman, coord);
697
698 /* Free canvas and canvas_info (\ref redraw) */
699 if(coord->flags & COF_OWN_CANVAS) {
700 canvas_free(_coord_get_canvas(coord));
701 coord_canvas_info_free(rdman, coord->canvas_info);
702 }
703
704 RM_CHILD(parent, coord);
705 subject_free(coord->mouse_event);
706 mb_prop_store_destroy(&coord->obj.props);
707 elmpool_elm_free(rdman->coord_pool, coord);
708 rdman->n_coords--;
709
710 return OK;
711 }
712
713 static int _rdman_coord_free_members(redraw_man_t *rdman, coord_t *coord) {
714 geo_t *member;
715 shape_t *shape;
716 int r;
717
718 FORMEMBERS(coord, member) {
719 shape = geo_get_shape(member);
720 r = rdman_shape_free(rdman, shape);
721 if(r != OK)
722 return ERR;
723 }
724 return OK;
725 }
726
727 /*! \brief Free descendant coords and shapes of a coord.
728 *
729 * The specified coord is also freed.
730 */
731 int rdman_coord_subtree_free(redraw_man_t *rdman, coord_t *subtree) {
732 coord_t *coord, *prev_coord;
733 int r;
734
735 if(subtree == NULL)
736 return OK;
737
738 prev_coord = postorder_coord_subtree(subtree, NULL);
739 for(coord = postorder_coord_subtree(subtree, prev_coord);
740 coord != NULL;
741 coord = postorder_coord_subtree(subtree, coord)) {
742 if(!(prev_coord->flags & COF_FREE)) {
743 r = _rdman_coord_free_members(rdman, prev_coord);
744 if(r != OK)
745 return ERR;
746
747 r = rdman_coord_free(rdman, prev_coord);
748 if(r != OK)
749 return ERR;
750 }
751 prev_coord = coord;
752 }
753 if(!(prev_coord->flags & COF_FREE)) {
754 r = _rdman_coord_free_members(rdman, prev_coord);
755 if(r != OK)
756 return ERR;
757
758 r = rdman_coord_free(rdman, prev_coord);
759 if(r != OK)
760 return ERR;
761 }
762
763 return OK;
764 }
765
766 /*! \brief Mark a coord is changed.
767 *
768 * A changed coord_t object is marked as dirty and put
769 * into dirty_coords list. rdman_coord_changed() should be called
770 * for a coord after it been changed to notify redraw manager to
771 * redraw shapes grouped by it.
772 */
773 int rdman_coord_changed(redraw_man_t *rdman, coord_t *coord) {
774 coord_t *child;
775
776 if(coord->flags & COF_DIRTY)
777 return OK;
778
779 add_dirty_coord(rdman, coord);
780
781 #if 0
782 if(coord->flags & COF_HIDDEN)
783 return OK;
784 #endif
785
786 /* Make child coords dirty. */
787 for(child = preorder_coord_subtree(coord, coord);
788 child != NULL;
789 child = preorder_coord_subtree(coord, child)) {
790 if(child->flags & (COF_DIRTY | COF_HIDDEN)) {
791 preorder_coord_skip_subtree(child);
792 continue;
793 }
794
795 add_dirty_coord(rdman, child);
796
797 if(child->flags & COF_OWN_CANVAS) {
798 preorder_coord_skip_subtree(child);
799 continue;
800 }
801 }
802
803 return OK;
804 }
805
806 static int _rdman_shape_changed(redraw_man_t *rdman, shape_t *shape) {
807 geo_t *geo;
808 int r;
809
810 geo = shape->geo;
811
812 if(geo->flags & GEF_DIRTY)
813 return OK;
814
815 r = add_dirty_geo(rdman, geo);
816 if(r == ERR)
817 return ERR;
818
819 return OK;
820 }
821
822 /*! \brief Mark a shape is changed.
823 *
824 * The geo_t object of a changed shape is mark as dirty and
825 * put into dirty_geos list.
826 */
827 int rdman_shape_changed(redraw_man_t *rdman, shape_t *shape) {
828 return _rdman_shape_changed(rdman, shape);
829 }
830
831 int rdman_paint_changed(redraw_man_t *rdman, paint_t *paint) {
832 shnode_t *shnode;
833 int r;
834
835 FORPAINTMEMBERS(paint, shnode) {
836 r = _rdman_shape_changed(rdman, shnode->shape);
837 if(r != OK)
838 return ERR;
839 }
840 return OK;
841 }
842
843 /* Clean dirties */
844
845 static int is_coord_subtree_hidden(coord_t *coord) {
846 while(coord) {
847 if(coord->flags & COF_HIDDEN)
848 return 1;
849 coord = coord->parent;
850 }
851 return 0;
852 }
853
854 static void clean_shape(shape_t *shape) {
855 switch(MBO_TYPE(shape)) {
856 case MBO_PATH:
857 sh_path_transform(shape);
858 break;
859 case MBO_TEXT:
860 sh_text_transform(shape);
861 break;
862 case MBO_RECT:
863 sh_rect_transform(shape);
864 break;
865 case MBO_IMAGE:
866 sh_image_transform(shape);
867 break;
868 #ifdef UNITTEST
869 default:
870 sh_dummy_transform(shape);
871 break;
872 #endif /* UNITTEST */
873 }
874 shape->geo->flags &= ~GEF_DIRTY;
875
876 if(is_coord_subtree_hidden(shape->coord))
877 sh_hide(shape);
878 else
879 sh_show(shape);
880 }
881
882 /*! \brief Setup canvas_info for the coord.
883 *
884 * Own a canvas or inherit it from parent.
885 * \sa
886 * - \ref redraw
887 */
888 static void setup_canvas_info(redraw_man_t *rdman, coord_t *coord) {
889 if(coord->parent == NULL)
890 return;
891
892 if(coord->opacity != 1 || coord_is_cached(coord)) {
893 if(!(coord->flags & COF_OWN_CANVAS)) {
894 /* canvas is assigned latter, in zeroing_coord() */
895 coord->canvas_info = coord_canvas_info_new(rdman, coord, NULL);
896 coord->flags |= COF_OWN_CANVAS;
897 }
898 } else {
899 if(coord->flags & COF_OWN_CANVAS) {
900 canvas_free(_coord_get_canvas(coord));
901 coord_canvas_info_free(rdman, coord->canvas_info);
902 coord->flags &= ~COF_OWN_CANVAS;
903 }
904 /* This must here to keep coords that do not own canvas
905 * can always point to right canvas_info. Since, they
906 * don't know when will parent change it's canvas_info.
907 */
908 coord->canvas_info = coord->parent->canvas_info;
909 }
910 }
911
912 static int coord_clean_members_n_compute_area(coord_t *coord) {
913 geo_t *geo;
914 /*! \note poses is shared by invokings, it is not support reentrying. */
915 static co_aix (*poses)[2];
916 static int max_poses = 0;
917 int cnt, pos_cnt;
918
919 /* Clean member shapes. */
920 cnt = 0;
921 FORMEMBERS(coord, geo) {
922 clean_shape(geo->shape);
923 cnt++;
924 }
925
926 if(max_poses < (cnt * 2)) {
927 free(poses);
928 max_poses = cnt * 2;
929 poses = (co_aix (*)[2])malloc(sizeof(co_aix [2]) * max_poses);
930 if(poses == NULL)
931 return ERR;
932 }
933
934 /* Compute area of the coord. */
935 pos_cnt = 0;
936 FORMEMBERS(coord, geo) {
937 area_to_positions(geo->cur_area, poses + pos_cnt);
938 pos_cnt += 2;
939 }
940
941 area_init(coord->cur_area, pos_cnt, poses);
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->canvas_info->owner_mems_area;
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(cur = preorder_coord_subtree(coord, coord);
981 cur != NULL;
982 cur = preorder_coord_subtree(coord, cur)) {
983 area = coord_get_area(cur);
984 if(area->x < min_x)
985 min_x = area->x;
986 if(area->y < min_y)
987 min_y = area->y;
988
989 x = area->x + area->w;
990 y = area->y + area->h;
991
992 if(x > max_x)
993 max_x = x;
994 if(y > max_y)
995 max_y = y;
996 if(cur->flags & COF_OWN_CANVAS)
997 preorder_coord_skip_subtree(cur);
998 }
999
1000 w = max_x - min_x;
1001 h = max_y - min_y;
1002
1003 /*
1004 * Setup area of the coord
1005 */
1006 aggr = coord_get_aggr_matrix(coord);
1007 x = y = 0;
1008 coord_trans_pos(coord->parent, &x, &y);
1009 poses[0][0] = x;
1010 poses[0][1] = y;
1011 x = w;
1012 y = h;
1013 coord_trans_pos(coord->parent, &x, &y);
1014 poses[1][0] = x;
1015 poses[1][1] = y;
1016
1017 area_init(coord_get_area(coord), 2, poses);
1018
1019 canvas = _coord_get_canvas(coord);
1020 if(canvas)
1021 canvas_get_size(canvas, &c_w, &c_h);
1022 else
1023 c_w = c_h = 0;
1024
1025 if(!coord_get_flags(coord, COF_JUST_CLEAN) &&
1026 min_x >= 0 && min_y >= 0 && max_x <= c_w && max_y <= c_h)
1027 /* Canvas fully cover sub-graphic. */
1028 return;
1029
1030 /*
1031 * Adjust matrics of descendants to align left-top corner of
1032 * minimum covering area with origin of space defined by
1033 * zeroing coord.
1034 */
1035 FOR_COORDS_PREORDER(coord, cur) {
1036 aggr = coord_get_aggr_matrix(cur);
1037 aggr[3] -= min_x;
1038 aggr[5] -= min_y;
1039 if(coord_get_flags(cur, COF_OWN_CANVAS)) {
1040 /*
1041 * Coords, zeroing, is zeroed in preorder of tree.
1042 * So, they are zeroed after ancesters with correctly
1043 * coord_t::aggr_matrix of parent coord to zeroing.
1044 */
1045 preorder_coord_skip_subtree(cur);
1046 area = coord_get_area(cur);
1047 area->x -= min_x;
1048 area->y -= min_y;
1049 } else
1050 coord_clean_members_n_compute_area(cur);
1051 }
1052
1053 /*
1054 * Setup canvas
1055 */
1056 if(canvas == NULL || w > c_w || h > c_w) {
1057 if(canvas)
1058 canvas_free(canvas);
1059 canvas = canvas_new(w, h);
1060 _coord_set_canvas(coord, canvas);
1061 }
1062
1063 area = &coord->canvas_info->cached_dirty_area;
1064 area->x = 0;
1065 area->y = 0;
1066 area->w = w;
1067 area->h = h;
1068 DARRAY_CLEAN(_coord_get_dirty_areas(coord));
1069 add_dirty_area(rdman, coord, area);
1070 }
1071
1072 /*! \brief Clean dirty coords.
1073 *
1074 * \note coords their opacity != 1 are also traded as cached ones.
1075 */
1076 static int clean_coord(redraw_man_t *rdman, coord_t *coord) {
1077 int r;
1078
1079 setup_canvas_info(rdman, coord);
1080
1081 if(coord->flags & COF_OWN_CANVAS)
1082 compute_aggr_of_cached_coord(coord);
1083 else
1084 compute_aggr_of_coord(coord);
1085
1086 /* Areas of cached coords are computed in two phase.
1087 * Phase 1 works like other normal ones. Phase 2, is collect
1088 * all areas of descendants to compute a minimum covering area.
1089 * Phase 2 is performed by zeroing_coord().
1090 */
1091 r = coord_clean_members_n_compute_area(coord);
1092 if(r != OK)
1093 return ERR;
1094
1095 coord->flags &= ~COF_DIRTY;
1096
1097 return OK;
1098 }
1099
1100 /*! \brief Clean coord_t objects.
1101 */
1102 static int clean_rdman_coords(redraw_man_t *rdman) {
1103 coord_t *coord;
1104 coord_t **dirty_coords;
1105 int n_dirty_coords;
1106 int i, r;
1107
1108 n_dirty_coords = rdman->dirty_coords.num;
1109 if(n_dirty_coords > 0) {
1110 dirty_coords = rdman->dirty_coords.ds;
1111 _insert_sort((void **)dirty_coords, n_dirty_coords,
1112 OFFSET(coord_t, order));
1113 for(i = 0; i < n_dirty_coords; i++) {
1114 coord = dirty_coords[i];
1115 if(!(coord->flags & COF_DIRTY))
1116 continue;
1117 r = clean_coord(rdman, coord);
1118 if(r != OK)
1119 return ERR;
1120 /* These two steps can be avoided for drawing all. */
1121 add_dirty_area(rdman, coord, &coord->areas[0]);
1122 add_dirty_area(rdman, coord, &coord->areas[1]);
1123 }
1124 }
1125 return OK;
1126 }
1127
1128 static int clean_rdman_geos(redraw_man_t *rdman) {
1129 int i;
1130 int n_dirty_geos;
1131 geo_t **dirty_geos;
1132 geo_t *visit_geo;
1133 coord_t *coord;
1134
1135 n_dirty_geos = rdman->dirty_geos.num;
1136 if(n_dirty_geos > 0) {
1137 dirty_geos = rdman->dirty_geos.ds;
1138 for(i = 0; i < n_dirty_geos; i++) {
1139 visit_geo = dirty_geos[i];
1140 if(!(visit_geo->flags & GEF_DIRTY))
1141 continue;
1142
1143 clean_shape(visit_geo->shape);
1144 coord = geo_get_coord(visit_geo);
1145 add_dirty_area(rdman, coord, visit_geo->cur_area);
1146 add_dirty_area(rdman, coord, visit_geo->last_area);
1147 }
1148 }
1149
1150 return OK;
1151 }
1152
1153 /*! \brief Add canvas owner of dirty geos to coord_t::zeroing_coords.
1154 *
1155 * All possible coords that need a zeroing have at least one dirty geo.
1156 */
1157 static int add_rdman_zeroing_coords(redraw_man_t *rdman) {
1158 int i;
1159 int n_dirty_geos;
1160 geo_t **dirty_geos, *geo;
1161 int n_dirty_coords;
1162 coord_t **dirty_coords, *coord;
1163
1164 n_dirty_geos = rdman->dirty_geos.num;
1165 dirty_geos = rdman->dirty_geos.ds;
1166 for(i = 0; i < n_dirty_geos; i++) {
1167 geo = dirty_geos[i];
1168 coord = geo_get_coord(geo)->canvas_info->owner;
1169 while(!coord_get_flags(coord, COF_MUST_ZEROING | COF_TEMP_MARK)) {
1170 coord_set_flags(coord, COF_TEMP_MARK);
1171 if(coord_is_root(coord))
1172 break;
1173 coord = coord->parent->canvas_info->owner;
1174 }
1175 }
1176
1177 n_dirty_coords = rdman->dirty_coords.num;
1178 dirty_coords = rdman->dirty_coords.ds;
1179 for(i = 0; i < n_dirty_coords; i++) {
1180 coord = dirty_coords[i]->canvas_info->owner;
1181 while(!coord_get_flags(coord, COF_MUST_ZEROING | COF_TEMP_MARK)) {
1182 coord_set_flags(coord, COF_TEMP_MARK);
1183 if(coord_is_root(coord))
1184 break;
1185 coord = coord->parent->canvas_info->owner;
1186 }
1187 }
1188
1189 FOR_COORDS_PREORDER(rdman->root_coord, coord) {
1190 if(!coord_get_flags(coord, COF_TEMP_MARK)) {
1191 preorder_coord_skip_subtree(coord);
1192 continue;
1193 }
1194 add_zeroing_coord(rdman, coord);
1195 coord_clear_flags(coord, COF_TEMP_MARK);
1196 }
1197
1198 return OK;
1199 }
1200
1201 /*! \brief Zeroing coords in redraw_man_t::zeroing_coords.
1202 *
1203 * \note redraw_man_t::zeroing_coords must in ascent partial order of tree.
1204 */
1205 static int zeroing_rdman_coords(redraw_man_t *rdman) {
1206 int i;
1207 coords_t *all_zeroing;
1208 coord_t *coord;
1209
1210 all_zeroing = &rdman->zeroing_coords;
1211 for(i = all_zeroing->num - 1; i >= 0; i--) {
1212 coord = all_zeroing->ds[i];
1213 if(coord_is_root(coord))
1214 continue;
1215 zeroing_coord(rdman, coord);
1216 }
1217
1218 return OK;
1219 }
1220
1221 /* \brief Compute matrix from cached canvas to parent device space.
1222 */
1223 static void compute_cached_2_pdev_matrix(coord_t *coord,
1224 co_aix canvas2pdev_matrix[6]) {
1225 coord_t *parent;
1226 co_aix *aggr;
1227 co_aix *matrix, *paggr;
1228 co_aix scale_x, scale_y;
1229 co_aix shift_x, shift_y;
1230 co_aix canvas2p[6];
1231
1232 aggr = coord_get_aggr_matrix(coord);
1233 matrix = coord->matrix;
1234 parent = coord->parent;
1235 paggr = coord_get_aggr_matrix(parent);
1236
1237 scale_x = matrix[0] / aggr[0];
1238 scale_y = matrix[3] / aggr[3];
1239 shift_x = matrix[2] - scale_x * aggr[2];
1240 shift_y = matrix[5] - scale_y * aggr[5];
1241
1242 canvas2p[0] = scale_x;
1243 canvas2p[1] = 0;
1244 canvas2p[2] = shift_x;
1245 canvas2p[3] = 0;
1246 canvas2p[4] = scale_y;
1247 canvas2p[5] = shift_y;
1248
1249 matrix_mul(paggr, canvas2p, canvas2pdev_matrix);
1250 }
1251
1252 /*! \brief Add aggregated dirty areas to ancestor.
1253 *
1254 * Dirty areas are aggregated into two areas. It assumes that even or odd
1255 * ones are old areas or new areas repsective. So, all even ones are
1256 * aggregated in an area, and odd ones are in another.
1257 */
1258 static void add_aggr_dirty_areas_to_ancestor(redraw_man_t *rdman,
1259 coord_t *coord) {
1260 int i;
1261 int n_areas;
1262 co_aix poses0[2][2], poses1[2][2];
1263 co_aix reverse[6];
1264 co_aix canvas2pdev_matrix[6];
1265 area_t **areas, *area;
1266 area_t *area0, *area1;
1267 coord_t *parent, *pcached_coord;
1268
1269 n_areas = _coord_get_dirty_areas(coord)->num;
1270 areas = _coord_get_dirty_areas(coord)->ds;
1271 if(n_areas == 0)
1272 abort(); /* should not happen! */
1273
1274 area0 = coord->canvas_info->aggr_dirty_areas;
1275 area1 = area0 + 1;
1276
1277 for(i = 0; i < n_areas; i++) {
1278 area = areas[i];
1279 if(area->w != 0 || area->h != 0)
1280 break;
1281 }
1282
1283 if(i < n_areas) {
1284 area = areas[i++];
1285 poses0[0][0] = area->x;
1286 poses0[0][1] = area->y;
1287 poses0[1][0] = area->x + area->w;
1288 poses0[1][1] = area->y + area->h;
1289 } else {
1290 poses0[0][0] = 0;
1291 poses0[0][1] = 0;
1292 poses0[1][0] = 0;
1293 poses0[1][1] = 0;
1294 }
1295
1296 if(i < n_areas) {
1297 area = areas[i++];
1298 poses1[0][0] = area->x;
1299 poses1[0][1] = area->y;
1300 poses1[1][0] = area->x + area->w;
1301 poses1[1][1] = area->y + area->h;
1302 } else {
1303 poses1[0][0] = 0;
1304 poses1[0][1] = 0;
1305 poses1[1][0] = 0;
1306 poses1[1][1] = 0;
1307 }
1308
1309 for(; i < n_areas - 1;) {
1310 /* Even areas */
1311 area = areas[i++];
1312 if(area->w != 0 || area->h != 0) {
1313 poses0[0][0] = MIN(poses0[0][0], area->x);
1314 poses0[0][1] = MIN(poses0[0][1], area->y);
1315 poses0[1][0] = MAX(poses0[1][0], area->x + area->w);
1316 poses0[1][1] = MAX(poses0[1][1], area->y + area->h);
1317 }
1318 /* Odd areas */
1319 area = areas[i++];
1320 if(area->w != 0 || area->h != 0) {
1321 poses1[0][0] = MIN(poses1[0][0], area->x);
1322 poses1[0][1] = MIN(poses1[0][1], area->y);
1323 poses1[1][0] = MAX(poses1[1][0], area->x + area->w);
1324 poses1[1][1] = MAX(poses1[1][1], area->y + area->h);
1325 }
1326 }
1327
1328 if(i < n_areas) {
1329 area = areas[i];
1330 if(area->w != 0 || area->h != 0) {
1331 poses0[0][0] = MIN(poses0[0][0], area->x);
1332 poses0[0][1] = MIN(poses0[0][1], area->y);
1333 poses0[1][0] = MAX(poses0[1][0], area->x + area->w);
1334 poses0[1][1] = MAX(poses0[1][1], area->y + area->h);
1335 }
1336 }
1337
1338 parent = coord->parent;
1339 pcached_coord = parent->canvas_info->owner;
1340
1341 compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
1342
1343 matrix_trans_pos(canvas2pdev_matrix, poses0[0], poses0[0] + 1);
1344 matrix_trans_pos(canvas2pdev_matrix, poses0[1], poses0[1] + 1);
1345 area_init(area0, 2, poses0);
1346 add_dirty_area(rdman, pcached_coord, area0);
1347
1348 matrix_trans_pos(canvas2pdev_matrix, poses1[0], poses1[0] + 1);
1349 matrix_trans_pos(canvas2pdev_matrix, poses1[1], poses1[1] + 1);
1350 area_init(area1, 2, poses1);
1351 if(area1->w != 0 || area1->h != 0)
1352 if(area0->x != area1->x || area0->y != area1->y ||
1353 area0->w != area1->w || area0->h != area1->h)
1354 add_dirty_area(rdman, pcached_coord, area1);
1355
1356 if(coord_get_flags(coord, COF_JUST_CLEAN) &&
1357 !coord_get_flags(pcached_coord, COF_JUST_CLEAN))
1358 add_dirty_area(rdman, pcached_coord, coord->last_area);
1359 }
1360
1361 static int add_rdman_aggr_dirty_areas(redraw_man_t *rdman) {
1362 int i;
1363 int n_zeroing;
1364 coord_t **zeroings;
1365 coord_t *coord;
1366
1367 n_zeroing = rdman->zeroing_coords.num;
1368 zeroings = rdman->zeroing_coords.ds;
1369 for(i = n_zeroing - 1; i >= 0; i--) {
1370 coord = zeroings[i];
1371 if(!coord_is_root(coord))
1372 add_aggr_dirty_areas_to_ancestor(rdman, coord);
1373 }
1374
1375 return OK;
1376 }
1377
1378 static int add_rdman_cached_dirty_areas(redraw_man_t *rdman) {
1379 int i;
1380 coord_t *coord, **dirty_coords;
1381 int n_dirty_coords;
1382
1383 n_dirty_coords = rdman->dirty_coords.num;
1384 dirty_coords = rdman->dirty_coords.ds;
1385 for(i = 0; i < n_dirty_coords; i++) {
1386 coord = dirty_coords[i];
1387 if(coord_get_flags(coord, COF_OWN_CANVAS)) {
1388 add_dirty_area(rdman, coord, coord->cur_area);
1389 add_dirty_area(rdman, coord, coord->last_area);
1390 }
1391 }
1392
1393 return OK;
1394 }
1395
1396 static int clean_rdman_dirties(redraw_man_t *rdman) {
1397 int r;
1398 int i;
1399 coord_t **coords, *coord;
1400 geo_t **geos;
1401
1402 /* coord_t::cur_area of coords are temporary pointed to
1403 * coord_canvas_info_t::owner_mems_area for store area
1404 * by clean_coor().
1405 */
1406 coords = rdman->dirty_coords.ds;
1407 for(i = 0; i < rdman->dirty_coords.num; i++) {
1408 coord = coords[i];
1409 if(coord->flags & COF_DIRTY) {
1410 if(!coord_get_flags(coord, COF_OWN_CANVAS))
1411 SWAP(coord->cur_area, coord->last_area, area_t *);
1412 else {
1413 coord->last_area = coord->cur_area;
1414 coord->cur_area = &coord->canvas_info->owner_mems_area;
1415 }
1416 }
1417 }
1418
1419 geos = rdman->dirty_geos.ds;
1420 for(i = 0; i < rdman->dirty_geos.num; i++)
1421 if(geos[i]->flags & GEF_DIRTY)
1422 SWAP(geos[i]->cur_area, geos[i]->last_area, area_t *);
1423
1424 r = clean_rdman_coords(rdman);
1425 if(r != OK)
1426 return ERR;
1427
1428 coords = rdman->dirty_coords.ds;
1429 for(i = 0; i < rdman->dirty_coords.num; i++) {
1430 coord = coords[i];
1431 coord_set_flags(coord, COF_JUST_CLEAN);
1432 coord->cur_area =
1433 (coord->last_area == coord->areas)?
1434 coord->areas + 1: coord->areas;
1435 }
1436
1437 r = clean_rdman_geos(rdman);
1438 if(r != OK)
1439 return ERR;
1440
1441 r = add_rdman_zeroing_coords(rdman);
1442 if(r != OK)
1443 return ERR;
1444
1445 r = zeroing_rdman_coords(rdman);
1446 if(r != OK)
1447 return ERR;
1448
1449 r = add_rdman_aggr_dirty_areas(rdman);
1450 if(r != OK)
1451 return ERR;
1452
1453 r = add_rdman_cached_dirty_areas(rdman);
1454 if(r != OK)
1455 return ERR;
1456
1457 coords = rdman->dirty_coords.ds;
1458 for(i = 0; i < rdman->dirty_coords.num; i++)
1459 coord_clear_flags(coords[i], COF_JUST_CLEAN);
1460
1461 return OK;
1462 }
1463
1464
1465 /* Drawing and Redrawing
1466 * ============================================================
1467 */
1468
1469 #ifndef UNITTEST
1470 static void set_shape_stroke_param(shape_t *shape, cairo_t *cr) {
1471 cairo_set_line_width(cr, shape->stroke_width);
1472 }
1473
1474 static void fill_path_preserve(redraw_man_t *rdman) {
1475 cairo_fill_preserve(rdman->cr);
1476 }
1477
1478 static void fill_path(redraw_man_t *rdman) {
1479 cairo_fill(rdman->cr);
1480 }
1481
1482 static void stroke_path(redraw_man_t *rdman) {
1483 cairo_stroke(rdman->cr);
1484 }
1485 #else
1486 static void set_shape_stroke_param(shape_t *shape, cairo_t *cr) {
1487 }
1488
1489 static void fill_path_preserve(redraw_man_t *rdman) {
1490 }
1491
1492 static void fill_path(redraw_man_t *rdman) {
1493 }
1494
1495 static void stroke_path(redraw_man_t *rdman) {
1496 }
1497 #endif
1498
1499 static void draw_shape(redraw_man_t *rdman, cairo_t *cr, shape_t *shape) {
1500 paint_t *fill, *stroke;
1501
1502 /*! \todo Move operator of shapes into singleton structures that define
1503 * operators for them.
1504 */
1505 if(shape->fill || shape->stroke) {
1506 switch(MBO_TYPE(shape)) {
1507 case MBO_PATH:
1508 sh_path_draw(shape, cr);
1509 break;
1510 case MBO_TEXT:
1511 sh_text_draw(shape, cr);
1512 break;
1513 case MBO_RECT:
1514 sh_rect_draw(shape, cr);
1515 break;
1516 case MBO_IMAGE:
1517 sh_image_draw(shape, cr);
1518 break;
1519 #ifdef UNITTEST
1520 default:
1521 sh_dummy_fill(shape, cr);
1522 break;
1523 #endif /* UNITTEST */
1524 }
1525
1526 fill = shape->fill;
1527 if(shape->fill) {
1528 fill->prepare(fill, cr);
1529 if(shape->stroke)
1530 fill_path_preserve(rdman);
1531 else
1532 fill_path(rdman);
1533 }
1534
1535 stroke = shape->stroke;
1536 if(stroke) {
1537 stroke->prepare(stroke, cr);
1538 set_shape_stroke_param(shape, cr);
1539 stroke_path(rdman);
1540 }
1541 }
1542 }
1543
1544 #ifndef UNITTEST
1545 static void clear_canvas(canvas_t *canvas) {
1546 cairo_operator_t old_op;
1547
1548 old_op = cairo_get_operator(canvas);
1549 cairo_set_operator(canvas, CAIRO_OPERATOR_CLEAR);
1550 cairo_paint(canvas);
1551 cairo_set_operator(canvas, old_op);
1552 }
1553
1554 static void make_clip(cairo_t *cr, int n_dirty_areas,
1555 area_t **dirty_areas) {
1556 int i;
1557 area_t *area;
1558
1559 cairo_new_path(cr);
1560 for(i = 0; i < n_dirty_areas; i++) {
1561 area = dirty_areas[i];
1562 cairo_rectangle(cr, area->x, area->y, area->w, area->h);
1563 }
1564 cairo_clip(cr);
1565 }
1566
1567 static void reset_clip(canvas_t *cr) {
1568 cairo_reset_clip(cr);
1569 }
1570
1571 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
1572 area_t **dirty_areas) {
1573 cairo_operator_t saved_op;
1574
1575 if(n_dirty_areas)
1576 make_clip(rdman->backend, n_dirty_areas, dirty_areas);
1577
1578 saved_op = cairo_get_operator(rdman->backend);
1579 cairo_set_operator(rdman->backend, CAIRO_OPERATOR_SOURCE);
1580 cairo_paint(rdman->backend);
1581 cairo_set_operator(rdman->backend, saved_op);
1582 }
1583 #else /* UNITTEST */
1584 static void clear_canvas(canvas_t *canvas) {
1585 }
1586
1587 static void reset_clip(canvas_t *cr) {
1588 }
1589
1590 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
1591 area_t **dirty_areas) {
1592 }
1593 #endif /* UNITTEST */
1594
1595 static int is_area_in_areas(area_t *area,
1596 int n_areas,
1597 area_t **areas) {
1598 int i;
1599
1600 for(i = 0; i < n_areas; i++) {
1601 if(areas_are_overlay(area, areas[i]))
1602 return 1;
1603 }
1604 return 0;
1605 }
1606
1607 static int is_geo_in_areas(geo_t *geo,
1608 int n_areas,
1609 area_t **areas) {
1610 return is_area_in_areas(geo->cur_area, n_areas, areas);
1611 }
1612
1613 static void update_cached_canvas_2_parent(redraw_man_t *rdman,
1614 coord_t *coord) {
1615 cairo_t *pcanvas, *canvas;
1616 cairo_surface_t *surface;
1617 cairo_pattern_t *pattern;
1618 cairo_matrix_t cr_matrix;
1619 co_aix reverse[6];
1620 co_aix canvas2pdev_matrix[6];
1621
1622 if(coord_is_root(coord))
1623 return;
1624
1625 compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
1626 compute_reverse(canvas2pdev_matrix, reverse);
1627
1628 cr_matrix.xx = reverse[0];
1629 cr_matrix.xy = reverse[1];
1630 cr_matrix.x0 = reverse[2];
1631 cr_matrix.yx = reverse[3];
1632 cr_matrix.yy = reverse[4];
1633 cr_matrix.y0 = reverse[5];
1634
1635 canvas = _coord_get_canvas(coord);
1636 pcanvas = _coord_get_canvas(coord->parent);
1637 surface = cairo_get_target(canvas);
1638 pattern = cairo_pattern_create_for_surface(surface);
1639 cairo_pattern_set_matrix(pattern, &cr_matrix);
1640 cairo_set_source(pcanvas, pattern);
1641 cairo_paint_with_alpha(pcanvas, coord->opacity);
1642 }
1643
1644 static int draw_coord_shapes_in_dirty_areas(redraw_man_t *rdman,
1645 coord_t *coord) {
1646 int dirty = 0;
1647 int r;
1648 area_t **areas;
1649 int n_areas;
1650 cairo_t *canvas;
1651 geo_t *member;
1652 coord_t *child;
1653 int mem_idx;
1654
1655 if(coord->flags & COF_HIDDEN)
1656 return OK;
1657
1658 areas = _coord_get_dirty_areas(coord)->ds;
1659 n_areas = _coord_get_dirty_areas(coord)->num;
1660 canvas = _coord_get_canvas(coord);
1661
1662 member = FIRST_MEMBER(coord);
1663 mem_idx = 0;
1664 child = FIRST_CHILD(coord);
1665 while(child != NULL || member != NULL) {
1666 if(child && child->before_pmem == mem_idx) {
1667 if(child->flags & COF_OWN_CANVAS) {
1668 if(!(child->flags & COF_HIDDEN) &&
1669 is_area_in_areas(coord_get_area(child), n_areas, areas)) {
1670 update_cached_canvas_2_parent(rdman, child);
1671 dirty = 1;
1672 }
1673 } else {
1674 r = draw_coord_shapes_in_dirty_areas(rdman, child);
1675 dirty |= r;
1676 }
1677 child = NEXT_CHILD(child);
1678 } else {
1679 ASSERT(member != NULL);
1680 if((!(member->flags & GEF_HIDDEN)) &&
1681 is_geo_in_areas(member, n_areas, areas)) {
1682 draw_shape(rdman, canvas, member->shape);
1683 dirty = 1;
1684 }
1685
1686 member = NEXT_MEMBER(member);
1687 mem_idx++;
1688 }
1689 }
1690
1691 return dirty;
1692 }
1693
1694 static int draw_dirty_cached_coord(redraw_man_t *rdman,
1695 coord_t *coord) {
1696 area_t **areas, *area;
1697 int n_areas;
1698 cairo_t *canvas;
1699 int i;
1700 int r;
1701
1702 areas = _coord_get_dirty_areas(coord)->ds;
1703 n_areas = _coord_get_dirty_areas(coord)->num;
1704
1705 for(i = 0; i < n_areas; i++) {
1706 area = areas[i];
1707 area->x = floorf(area->x);
1708 area->y = floorf(area->y);
1709 area->w = ceilf(area->w);
1710 area->h = ceilf(area->h);
1711 }
1712
1713 canvas = _coord_get_canvas(coord);
1714 make_clip(canvas, n_areas, areas);
1715 clear_canvas(canvas);
1716
1717 r = draw_coord_shapes_in_dirty_areas(rdman, coord);
1718
1719 reset_clip(canvas);
1720 }
1721
1722 static void draw_shapes_in_dirty_areas(redraw_man_t *rdman) {
1723 int i;
1724 coord_t *coord;
1725
1726 for(i = rdman->zeroing_coords.num - 1; i >= 0; i--) {
1727 coord = rdman->zeroing_coords.ds[i];
1728 draw_dirty_cached_coord(rdman, coord);
1729 }
1730 }
1731
1732
1733 /*! \brief Re-draw all changed shapes or shapes affected by changed coords.
1734 *
1735 * A coord object has a geo to keep track the range that it's members will
1736 * draw on. Geo of a coord should be recomputed when the coord is changed.
1737 * Geo of a coord used to accelerate finding overlay shape objects of
1738 * a specified geo. A coord object also must be recomputed when one of
1739 * it's members is changed.
1740 *
1741 * New and old geo values of a coord object that is recomputed for
1742 * changing of it-self must be used to find overlay shape objects.
1743 * New and old geo values of a shape should also be used to find
1744 * overlay shape objects, too. If a shape's coord is changed, shape's
1745 * geo object is not used to find overlay shape objects any more.
1746 *
1747 * steps:
1748 * - update chagned coord objects
1749 * - recompute area for changed coord objects
1750 * - recompute geo for members shape objects
1751 * - clear dirty of geo for members to prevent from
1752 * recomputing for change of shape objects.
1753 * - add old and new area value to list of dirty areas.
1754 * - recompute geo for changed shape objects
1755 * - only if a shape object is dirty.
1756 * - put new and old value of area of geo to list of dirty areas.
1757 * - Scan all shapes and redraw shapes overlaid with dirty areas.
1758 *
1759 * dirty flag of coord objects is cleared after update.
1760 * dirty flag of geo objects is also cleared after recomputing.
1761 * Clean dirty flag can prevent redundant computing for geo and
1762 * corod objects.
1763 *
1764 */
1765 int rdman_redraw_changed(redraw_man_t *rdman) {
1766 int r;
1767 event_t event;
1768 subject_t *redraw;
1769 int i;
1770 coord_t *coord, **coords;
1771 int n_areas;
1772 area_t **areas;
1773
1774 r = clean_rdman_dirties(rdman);
1775 if(r != OK)
1776 return ERR;
1777
1778 if(rdman->n_dirty_areas > 0) {
1779 /*! \brief Draw shapes in preorder of coord tree and support opacity
1780 * rules.
1781 */
1782 draw_shapes_in_dirty_areas(rdman);
1783 n_areas = _coord_get_dirty_areas(rdman->root_coord)->num;
1784 areas = _coord_get_dirty_areas(rdman->root_coord)->ds;
1785 copy_cr_2_backend(rdman, n_areas, areas);
1786 reset_clip(rdman->backend);
1787 for(i = 0; i < rdman->zeroing_coords.num; i++) {
1788 coord = rdman->zeroing_coords.ds[i];
1789 DARRAY_CLEAN(_coord_get_dirty_areas(coord));
1790 }
1791 rdman->n_dirty_areas = 0;
1792 }
1793
1794 coords = rdman->zeroing_coords.ds;
1795 for(i = 0; i < rdman->zeroing_coords.num; i++) {
1796 coord = coords[i];
1797 coord_clear_flags(coord, COF_MUST_ZEROING);
1798 }
1799
1800 DARRAY_CLEAN(&rdman->dirty_coords);
1801 DARRAY_CLEAN(&rdman->dirty_geos);
1802 DARRAY_CLEAN(&rdman->zeroing_coords);
1803
1804 /* Free postponsed removing */
1805 free_free_objs(rdman);
1806
1807 redraw = rdman_get_redraw_subject(rdman);
1808 event.type = EVT_RDMAN_REDRAW;
1809 event.tgt = event.cur_tgt = redraw;
1810 subject_notify(redraw, &event);
1811
1812 return OK;
1813 }
1814
1815 /* NOTE: Before redrawing, the canvas/surface must be cleaned.
1816 * NOTE: After redrawing, the content must be copied to the backend surface.
1817 */ 64 */
1818 65
1819 /*! \page redraw How to Redraw Shapes? 66 /*! \page redraw How to Redraw Shapes?
1820 * 67 *
1821 * Coords are corresponding objects for group tags of SVG files. 68 * Coords are corresponding objects for group tags of SVG files.
2042 * - draw dirty areas 289 * - draw dirty areas
2043 * - areas are rounded to N at first. 290 * - areas are rounded to N at first.
2044 * - from leaf to root. 291 * - from leaf to root.
2045 */ 292 */
2046 293
294 #ifndef ASSERT
295 #define ASSERT(x)
296 #endif
297
298 /* NOTE: bounding box should also consider width of stroke.
299 */
300
301 #define sh_attach_geo(sh, g) \
302 do { \
303 (sh)->geo = g; \
304 (g)->shape = (shape_t *)(sh); \
305 } while(0)
306 #define sh_detach_geo(sh) \
307 do { \
308 (sh)->geo->shape = NULL; \
309 (sh)->geo = NULL; \
310 } while(0)
311 #define sh_get_geo(sh) ((sh)->geo)
312 #define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0)
313 #define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0)
314 #define rdman_is_dirty(rdman) \
315 ((rdman)->dirty_coords.num != 0 || \
316 (rdman)->dirty_geos.num != 0)
317
318 #define OK 0
319 #define ERR -1
320
321 #define ARRAY_EXT_SZ 64
322
323 #define SWAP(a, b, t) do { t c; c = a; a = b; b = c; } while(0)
324
325 #ifdef UNITTEST
326 typedef struct _sh_dummy sh_dummy_t;
327
328 extern void sh_dummy_transform(shape_t *shape);
329 extern void sh_dummy_fill(shape_t *, cairo_t *);
330 #endif /* UNITTEST */
331
332 static subject_t *ob_subject_alloc(ob_factory_t *factory);
333 static void ob_subject_free(ob_factory_t *factory, subject_t *subject);
334 static observer_t *ob_observer_alloc(ob_factory_t *factory);
335 static void ob_observer_free(ob_factory_t *factory, observer_t *observer);
336 static subject_t *ob_get_parent_subject(ob_factory_t *factory,
337 subject_t *cur_subject);
338 /* Functions for children. */
339 #define FORCHILDREN(coord, child) \
340 for((child) = STAILQ_HEAD((coord)->children); \
341 (child) != NULL; \
342 (child) = STAILQ_NEXT(coord_t, sibling, (child)))
343 #define NEXT_CHILD(child) STAILQ_NEXT(coord_t, sibling, child)
344 #define ADD_CHILD(parent, child) \
345 STAILQ_INS_TAIL((parent)->children, coord_t, sibling, (child))
346 #define RM_CHILD(parent, child) \
347 STAILQ_REMOVE((parent)->children, coord_t, sibling, (child))
348 #define FIRST_CHILD(parent) STAILQ_HEAD((parent)->children)
349
350 /* Functions for members. */
351 #define FORMEMBERS(coord, member) \
352 for((member) = STAILQ_HEAD((coord)->members); \
353 (member) != NULL; \
354 (member) = STAILQ_NEXT(geo_t, coord_next, (member)))
355 #define NEXT_MEMBER(member) STAILQ_NEXT(geo_t, coord_next, (member))
356 #define ADD_MEMBER(coord, member) \
357 STAILQ_INS_TAIL((coord)->members, geo_t, coord_next, (member))
358 #define RM_MEMBER(coord, member) \
359 STAILQ_REMOVE((coord)->members, geo_t, coord_next, (member))
360 #define FIRST_MEMBER(coord) STAILQ_HEAD((coord)->members)
361
362 /* Functions for paint members. */
363 #define FORPAINTMEMBERS(paint, member) \
364 for((member) = STAILQ_HEAD((paint)->members); \
365 (member) != NULL; \
366 (member) = STAILQ_NEXT(paint_t, next, member))
367 #define RM_PAINTMEMBER(paint, member) \
368 STAILQ_REMOVE((paint)->members, shnode_t, next, member)
369 #define RM_PAINT(rdman, paint) \
370 STAILQ_REMOVE((rdman)->paints, paint_t, pnt_next, paint)
371
372 /*! \brief Sort a list of element by a unsigned integer.
373 *
374 * The result is in ascend order. The unsigned integers is
375 * at offset specified by 'off' from start address of elemnts.
376 */
377 static void _insert_sort(void **elms, int num, int off) {
378 int i, j;
379 unsigned int val;
380 void *elm_i;
381
382 for(i = 1; i < num; i++) {
383 elm_i = elms[i];
384 val = *(unsigned int *)(elm_i + off);
385 for(j = i; j > 0; j--) {
386 if(*(unsigned int *)(elms[j - 1] + off) <= val)
387 break;
388 elms[j] = elms[j - 1];
389 }
390 elms[j] = elm_i;
391 }
392 }
393
394 DARRAY_DEFINE(coords, coord_t *);
395 DARRAY_DEFINE(geos, geo_t *);
396 DARRAY_DEFINE(areas, area_t *);
397
398 int rdman_add_gen_geos(redraw_man_t *rdman, geo_t *geo) {
399 int r;
400
401 r = geos_add(rdman_get_gen_geos(rdman), geo);
402 return r;
403 }
404
405 /*! Use \brief DARRAY to implement dirty & free lists.
406 */
407 #define ADD_DATA(sttype, field, v) \
408 int r; \
409 r = sttype ## _add(&rdman->field, v); \
410 return r == 0? OK: ERR;
411
412
413 static int is_area_in_areas(area_t *area,
414 int n_areas,
415 area_t **areas) {
416 int i;
417
418 for(i = 0; i < n_areas; i++) {
419 if(areas_are_overlay(area, areas[i]))
420 return 1;
421 }
422 return 0;
423 }
424
425 static int is_geo_in_areas(geo_t *geo,
426 int n_areas,
427 area_t **areas) {
428 return is_area_in_areas(geo->cur_area, n_areas, areas);
429 }
430
431 static void area_to_positions(area_t *area, co_aix (*poses)[2]) {
432 poses[0][0] = area->x;
433 poses[0][1] = area->y;
434 poses[1][0] = area->x + area->w;
435 poses[1][1] = area->y + area->h;;
436 }
437
438 /* Maintain Lists */
439
440 static int add_dirty_coord(redraw_man_t *rdman, coord_t *coord) {
441 coord->flags |= COF_DIRTY;
442 ADD_DATA(coords, dirty_coords, coord);
443 return OK;
444 }
445
446 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
447 geo->flags |= GEF_DIRTY;
448 ADD_DATA(geos, dirty_geos, geo);
449 return OK;
450 }
451
452 static int add_dirty_area(redraw_man_t *rdman, coord_t *coord, area_t *area) {
453 int r;
454
455 rdman->n_dirty_areas++;
456 r = areas_add(_coord_get_dirty_areas(coord), area);
457 return r == 0? OK: ERR;
458 }
459
460 static int add_zeroing_coord(redraw_man_t *rdman, coord_t *coord) {
461 coord_set_zeroing(coord);
462 ADD_DATA(coords, zeroing_coords, coord);
463 return OK;
464 }
465
466 static int add_free_obj(redraw_man_t *rdman, void *obj,
467 free_func_t free_func) {
468 int max;
469 free_obj_t *new_objs, *free_obj;
470
471 if(rdman->free_objs.num >= rdman->free_objs.max) {
472 max = rdman->free_objs.num + ARRAY_EXT_SZ;
473 new_objs = realloc(rdman->free_objs.objs,
474 max * sizeof(free_obj_t));
475 if(new_objs == NULL)
476 return ERR;
477 rdman->free_objs.max = max;
478 rdman->free_objs.objs = new_objs;
479 }
480
481 free_obj = rdman->free_objs.objs + rdman->free_objs.num++;
482 free_obj->obj = obj;
483 free_obj->free_func = free_func;
484
485 return OK;
486 }
487
488 static void free_free_objs(redraw_man_t *rdman) {
489 int i;
490 free_obj_t *free_obj;
491
492 for(i = 0; i < rdman->free_objs.num; i++) {
493 free_obj = &rdman->free_objs.objs[i];
494 free_obj->free_func(rdman, free_obj->obj);
495 }
496 rdman->free_objs.num = 0;
497 }
498
499 static void free_objs_destroy(redraw_man_t *rdman) {
500 if(rdman->free_objs.objs != NULL)
501 free(rdman->free_objs.objs);
502 }
503
504
505
506 static cairo_t *canvas_new(int w, int h) {
507 #ifndef UNITTEST
508 cairo_surface_t *surface;
509 cairo_t *cr;
510
511 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
512 w, h);
513 cr = cairo_create(surface);
514
515 return cr;
516 #else
517 return NULL;
518 #endif
519 }
520
521 static void canvas_free(cairo_t *canvas) {
522 #ifndef UNITTEST
523 cairo_destroy(canvas);
524 #endif
525 }
526
527 static void canvas_get_size(cairo_t *canvas, int *w, int *h) {
528 #ifndef UNITTEST
529 cairo_surface_t *surface;
530
531 surface = cairo_get_target(canvas);
532 *w = cairo_image_surface_get_width(surface);
533 *h = cairo_image_surface_get_height(surface);
534 #else
535 *w = 0;
536 *h = 0;
537 #endif
538 }
539
540 static int geo_off_in_coord(geo_t *geo, coord_t *coord) {
541 int off = 0;
542 geo_t *vgeo;
543
544 FORMEMBERS(coord, vgeo) {
545 if(vgeo == geo)
546 return off;
547 off++;
548 }
549 return -1;
550 }
551
552 static void geo_attach_coord(geo_t *geo, coord_t *coord) {
553 ADD_MEMBER(coord, geo);
554 coord->num_members++;
555 }
556
557 static void geo_detach_coord(geo_t *geo, coord_t *coord) {
558 int off;
559 coord_t *child;
560
561 off = geo_off_in_coord(geo, coord);
562 if(off < 0)
563 return;
564 FORCHILDREN(coord, child) {
565 if(child->before_pmem >= off)
566 child->before_pmem--;
567 }
568
569 RM_MEMBER(coord, geo);
570 coord->num_members--;
571 }
572
573 static coord_canvas_info_t *coord_canvas_info_new(redraw_man_t *rdman,
574 coord_t *coord,
575 cairo_t *canvas) {
576 coord_canvas_info_t *info;
577
578 info = (coord_canvas_info_t *)elmpool_elm_alloc(rdman->coord_canvas_pool);
579 if(info == NULL)
580 return info;
581
582 info->owner = coord;
583 info->canvas = canvas;
584 DARRAY_INIT(&info->dirty_areas);
585
586 return info;
587 }
588
589 static void coord_canvas_info_free(redraw_man_t *rdman,
590 coord_canvas_info_t *info) {
591 DARRAY_DESTROY(&info->dirty_areas);
592 elmpool_elm_free(rdman->coord_canvas_pool, info);
593 }
594
595 static void mouse_event_root_dummy(event_t *evt, void *arg) {
596 }
597
598 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr, cairo_t *backend) {
599 extern void redraw_man_destroy(redraw_man_t *rdman);
600 extern int _paint_color_size;
601 observer_t *addrm_ob;
602 extern void addrm_monitor_hdlr(event_t *evt, void *arg);
603
604 memset(rdman, 0, sizeof(redraw_man_t));
605
606 DARRAY_INIT(&rdman->dirty_coords);
607 DARRAY_INIT(&rdman->dirty_geos);
608 DARRAY_INIT(&rdman->gen_geos);
609 DARRAY_INIT(&rdman->zeroing_coords);
610
611 rdman->geo_pool = elmpool_new(sizeof(geo_t), 128);
612 rdman->coord_pool = elmpool_new(sizeof(coord_t), 16);
613 rdman->shnode_pool = elmpool_new(sizeof(shnode_t), 16);
614 rdman->observer_pool = elmpool_new(sizeof(observer_t), 32);
615 rdman->subject_pool = elmpool_new(sizeof(subject_t), 32);
616 rdman->paint_color_pool = elmpool_new(_paint_color_size, 64);
617 rdman->pent_pool = elmpool_new(sizeof(mb_prop_entry_t), 128);
618 rdman->coord_canvas_pool = elmpool_new(sizeof(coord_canvas_info_t), 16);
619 if(!(rdman->geo_pool && rdman->coord_pool && rdman->shnode_pool &&
620 rdman->observer_pool && rdman->subject_pool &&
621 rdman->paint_color_pool && rdman->coord_canvas_pool))
622 goto err;
623
624 rdman->ob_factory.subject_alloc = ob_subject_alloc;
625 rdman->ob_factory.subject_free = ob_subject_free;
626 rdman->ob_factory.observer_alloc = ob_observer_alloc;
627 rdman->ob_factory.observer_free = ob_observer_free;
628 rdman->ob_factory.get_parent_subject = ob_get_parent_subject;
629
630 rdman->redraw =
631 subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
632 rdman->addrm_monitor =
633 subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
634 if(!(rdman->redraw && rdman->addrm_monitor))
635 goto err;
636
637 addrm_ob = subject_add_observer(rdman->addrm_monitor,
638 addrm_monitor_hdlr, rdman);
639 if(addrm_ob == NULL)
640 goto err;
641
642 rdman->last_mouse_over = NULL;
643
644 rdman->root_coord = elmpool_elm_alloc(rdman->coord_pool);
645 if(rdman->root_coord == NULL)
646 redraw_man_destroy(rdman);
647 rdman->n_coords = 1;
648 coord_init(rdman->root_coord, NULL);
649 mb_prop_store_init(&rdman->root_coord->obj.props, rdman->pent_pool);
650 rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory,
651 rdman->root_coord,
652 OBJT_COORD);
653 rdman->root_coord->flags |= COF_OWN_CANVAS;
654 rdman->root_coord->canvas_info =
655 coord_canvas_info_new(rdman, rdman->root_coord, cr);
656 rdman->root_coord->opacity = 1;
657
658 rdman->cr = cr;
659 rdman->backend = backend;
660
661 STAILQ_INIT(rdman->shapes);
662 STAILQ_INIT(rdman->paints);
663
664 /* \note To make root coord always have at leat one observer.
665 * It triggers mouse interpreter to be installed on root.
666 */
667 subject_set_monitor(rdman->root_coord->mouse_event,
668 rdman->addrm_monitor);
669 subject_add_observer(rdman->root_coord->mouse_event,
670 mouse_event_root_dummy, NULL);
671
672 mb_prop_store_init(&rdman->props, rdman->pent_pool);
673 return OK;
674
675 err:
676 if(rdman->geo_pool)
677 elmpool_free(rdman->geo_pool);
678 if(rdman->coord_pool)
679 elmpool_free(rdman->coord_pool);
680 if(rdman->shnode_pool)
681 elmpool_free(rdman->shnode_pool);
682 if(rdman->observer_pool)
683 elmpool_free(rdman->observer_pool);
684 if(rdman->subject_pool)
685 elmpool_free(rdman->subject_pool);
686 if(rdman->paint_color_pool)
687 elmpool_free(rdman->paint_color_pool);
688 if(rdman->pent_pool)
689 elmpool_free(rdman->pent_pool);
690 if(rdman->coord_canvas_pool)
691 elmpool_free(rdman->coord_canvas_pool);
692 DARRAY_DESTROY(&rdman->dirty_coords);
693 DARRAY_DESTROY(&rdman->dirty_geos);
694 DARRAY_DESTROY(&rdman->gen_geos);
695 DARRAY_DESTROY(&rdman->zeroing_coords);
696 return ERR;
697 }
698
699 void redraw_man_destroy(redraw_man_t *rdman) {
700 coord_t *coord, *saved_coord;
701 shape_t *shape, *saved_shape;
702 geo_t *member;
703
704 mb_prop_store_destroy(&rdman->props);
705
706 free_free_objs(rdman);
707 free_objs_destroy(rdman);
708
709 coord = postorder_coord_subtree(rdman->root_coord, NULL);
710 while(coord) {
711 saved_coord = coord;
712 coord = postorder_coord_subtree(rdman->root_coord, coord);
713 FORMEMBERS(saved_coord, member) {
714 rdman_shape_free(rdman, member->shape);
715 }
716 rdman_coord_free(rdman, saved_coord);
717 }
718 #if 0
719 FORMEMBERS(saved_coord, member) {
720 rdman_shape_free(rdman, member->shape);
721 }
722 #endif
723 /* Resources of root_coord is free by elmpool_free() or
724 * caller; for canvas
725 */
726
727 shape = saved_shape = STAILQ_HEAD(rdman->shapes);
728 while(shape && (shape = STAILQ_NEXT(shape_t, sh_next, shape))) {
729 rdman_shape_free(rdman, saved_shape);
730 #if 0
731 STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, saved_shape);
732 #endif
733 saved_shape = shape;
734 }
735 if(saved_shape != NULL)
736 rdman_shape_free(rdman, saved_shape);
737
738 coord_canvas_info_free(rdman, rdman->root_coord->canvas_info);
739
740 elmpool_free(rdman->coord_pool);
741 elmpool_free(rdman->geo_pool);
742 elmpool_free(rdman->shnode_pool);
743 elmpool_free(rdman->observer_pool);
744 elmpool_free(rdman->subject_pool);
745 elmpool_free(rdman->paint_color_pool);
746 elmpool_free(rdman->pent_pool);
747 elmpool_free(rdman->coord_canvas_pool);
748
749 DARRAY_DESTROY(&rdman->dirty_coords);
750 DARRAY_DESTROY(&rdman->dirty_geos);
751 DARRAY_DESTROY(&rdman->gen_geos);
752 DARRAY_DESTROY(&rdman->zeroing_coords);
753 }
754
755
756 #define ASSERT(x)
757 /*
758 * Change transformation matrix
759 * - update aggregated transformation matrix
760 * - of coord_t object been changed.
761 * - of children coord_t objects.
762 * - redraw members of coord_t objects.
763 * - redraw shape objects they are overlaid with members.
764 * - find out overlaid shape objects.
765 * - geo_t of a coord_t object
766 * - can make finding more efficiency.
767 * - fill overlay geo_t objects of members.
768 *
769 * Change a shape object
770 * - redraw changed object.
771 * - redraw shape objects they are overlaid with changed object.
772 * - find out overlaid shape objects.
773 *
774 * That coord and geo of shape objects are setted by user code
775 * give user code a chance to collect coord and geo objects together
776 * and gain interest of higher cache hit rate.
777 */
778
779 int rdman_add_shape(redraw_man_t *rdman, shape_t *shape, coord_t *coord) {
780 geo_t *geo;
781 int r;
782
783 geo = elmpool_elm_alloc(rdman->geo_pool);
784 if(geo == NULL)
785 return ERR;
786
787 geo_init(geo);
788 geo->mouse_event = subject_new(&rdman->ob_factory, geo, OBJT_GEO);
789 subject_set_monitor(geo->mouse_event, rdman->addrm_monitor);
790
791 geo_attach_coord(geo, coord);
792
793 /* New one should be dirty to recompute it when drawing. */
794 r = add_dirty_geo(rdman, geo);
795 if(r != OK)
796 return ERR;
797
798 sh_attach_coord(shape, coord);
799 sh_attach_geo(shape, geo);
800
801 return OK;
802 }
803
804 /*! \brief Remove a shape object from redraw manager.
805 *
806 * \note Shapes should be removed after redrawing or when rdman is in clean.
807 * \note Removing shapes or coords when a rdman is dirty, removing
808 * is postponsed.
809 * \todo redraw shape objects that overlaid with removed one.
810 */
811 int rdman_shape_free(redraw_man_t *rdman, shape_t *shape) {
812 geo_t *geo;
813 int r;
814
815 geo = shape->geo;
816
817 if(rdman_is_dirty(rdman) && geo != NULL) {
818 if(geo->flags & GEF_FREE)
819 return ERR;
820
821 geo->flags |= GEF_FREE;
822 sh_hide(shape);
823 if(!(geo->flags & GEF_DIRTY)) {
824 r = add_dirty_geo(rdman, geo);
825 if(r != OK)
826 return ERR;
827 }
828 r = add_free_obj(rdman, shape, (free_func_t)rdman_shape_free);
829 if(r != OK)
830 return ERR;
831 return OK;
832 }
833
834 if(geo != NULL) {
835 subject_free(geo->mouse_event);
836 geo_detach_coord(geo, shape->coord);
837 sh_detach_coord(shape);
838 sh_detach_geo(shape);
839 elmpool_elm_free(rdman->geo_pool, geo);
840 }
841 STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, shape);
842 mb_prop_store_destroy(&shape->obj.props);
843 shape->free(shape);
844
845 if(rdman->last_mouse_over == (mb_obj_t *)shape)
846 rdman->last_mouse_over = NULL;
847
848 return OK;
849 }
850
851 shnode_t *shnode_new(redraw_man_t *rdman, shape_t *shape) {
852 shnode_t *node;
853
854 node = (shnode_t *)elmpool_elm_alloc(rdman->shnode_pool);
855 if(node) {
856 node->shape = shape;
857 node->next = NULL;
858 }
859 return node;
860 }
861
862 int rdman_paint_free(redraw_man_t *rdman, paint_t *paint) {
863 shnode_t *shnode, *saved_shnode;
864
865 if(rdman_is_dirty(rdman)) {
866 if(!(paint->flags & PNTF_FREE))
867 return ERR;
868 add_free_obj(rdman, paint, (free_func_t)rdman_paint_free);
869 paint->flags |= PNTF_FREE;
870 return OK;
871 }
872
873 /* Free member shapes that using this paint. */
874 saved_shnode = NULL;
875 FORPAINTMEMBERS(paint, shnode) {
876 if(saved_shnode) {
877 RM_PAINTMEMBER(paint, saved_shnode);
878 shnode_free(rdman, saved_shnode);
879 }
880 saved_shnode = shnode;
881 }
882 if(saved_shnode) {
883 RM_PAINTMEMBER(paint, saved_shnode);
884 shnode_free(rdman, saved_shnode);
885 }
886
887 RM_PAINT(rdman, paint);
888 paint->free(rdman, paint);
889 return OK;
890 }
891
892 void _rdman_paint_real_remove_child(redraw_man_t *rdman,
893 paint_t *paint,
894 shape_t *shape) {
895 shnode_t *shnode;
896
897 FORPAINTMEMBERS(paint, shnode) {
898 if(shnode->shape == shape) {
899 RM_PAINTMEMBER(paint, shnode);
900 shnode_free(rdman, shnode);
901 break;
902 }
903 }
904 }
905
906 coord_t *rdman_coord_new(redraw_man_t *rdman, coord_t *parent) {
907 coord_t *coord, *root_coord;
908 coord_t *visit;
909
910 coord = elmpool_elm_alloc(rdman->coord_pool);
911 if(coord == NULL)
912 return NULL;
913
914 coord_init(coord, parent);
915 mb_prop_store_init(&coord->obj.props, rdman->pent_pool);
916 coord->mouse_event = subject_new(&rdman->ob_factory,
917 coord,
918 OBJT_COORD);
919 subject_set_monitor(coord->mouse_event, rdman->addrm_monitor);
920 /*! \note default opacity == 1 */
921 coord->opacity = 1;
922 if(parent)
923 coord->canvas_info = parent->canvas_info;
924 rdman->n_coords++;
925
926 coord->order = ++rdman->next_coord_order;
927 if(coord->order == 0) {
928 rdman->next_coord_order = 0;
929 root_coord = visit = rdman->root_coord;
930 /* skip root coord. */
931 visit = preorder_coord_subtree(root_coord, visit);
932 while(visit) {
933 visit->order = ++rdman->next_coord_order;
934 visit = preorder_coord_subtree(root_coord, visit);
935 }
936 }
937
938 coord->before_pmem = parent->num_members;
939
940 /* If parent is dirty, children should be dirty. */
941 if(parent && (parent->flags & COF_DIRTY))
942 add_dirty_coord(rdman, coord);
943
944 return coord;
945 }
946
947 static int rdman_coord_free_postponse(redraw_man_t *rdman, coord_t *coord) {
948 int r;
949
950 if(coord->flags & COF_FREE)
951 return ERR;
952
953 coord->flags |= COF_FREE;
954 coord_hide(coord);
955 if(!(coord->flags & COF_DIRTY)) {
956 r = add_dirty_coord(rdman, coord);
957 if(r != OK)
958 return ERR;
959 }
960 r = add_free_obj(rdman, coord, (free_func_t)rdman_coord_free);
961 if(r != OK)
962 return ERR;
963 return OK;
964 }
965
966 /*! \brief Free a coord of a redraw_man_t object.
967 *
968 * All children and members should be freed before parent being freed.
969 *
970 * \param coord is a coord_t without children and members.
971 * \return 0 for successful, -1 for error.
972 *
973 * \note Free is postponsed if the coord is dirty or it has children
974 * or members postponsed for free.
975 */
976 int rdman_coord_free(redraw_man_t *rdman, coord_t *coord) {
977 coord_t *parent;
978 coord_t *child;
979 geo_t *member;
980 int cm_cnt; /* children & members counter */
981
982 parent = coord->parent;
983 if(parent == NULL)
984 return ERR;
985
986 cm_cnt = 0;
987 FORCHILDREN(coord, child) {
988 cm_cnt++;
989 if(!(child->flags & COF_FREE))
990 return ERR;
991 }
992 FORMEMBERS(coord, member) {
993 cm_cnt++;
994 if(!(member->flags & GEF_FREE))
995 return ERR;
996 }
997
998 if(cm_cnt || rdman_is_dirty(rdman))
999 return rdman_coord_free_postponse(rdman, coord);
1000
1001 /* Free canvas and canvas_info (\ref redraw) */
1002 if(coord->flags & COF_OWN_CANVAS) {
1003 canvas_free(_coord_get_canvas(coord));
1004 coord_canvas_info_free(rdman, coord->canvas_info);
1005 }
1006
1007 RM_CHILD(parent, coord);
1008 subject_free(coord->mouse_event);
1009 mb_prop_store_destroy(&coord->obj.props);
1010 elmpool_elm_free(rdman->coord_pool, coord);
1011 rdman->n_coords--;
1012
1013 return OK;
1014 }
1015
1016 static int _rdman_coord_free_members(redraw_man_t *rdman, coord_t *coord) {
1017 geo_t *member;
1018 shape_t *shape;
1019 int r;
1020
1021 FORMEMBERS(coord, member) {
1022 shape = geo_get_shape(member);
1023 r = rdman_shape_free(rdman, shape);
1024 if(r != OK)
1025 return ERR;
1026 }
1027 return OK;
1028 }
1029
1030 /*! \brief Free descendant coords and shapes of a coord.
1031 *
1032 * The specified coord is also freed.
1033 */
1034 int rdman_coord_subtree_free(redraw_man_t *rdman, coord_t *subtree) {
1035 coord_t *coord, *prev_coord;
1036 int r;
1037
1038 if(subtree == NULL)
1039 return OK;
1040
1041 prev_coord = postorder_coord_subtree(subtree, NULL);
1042 for(coord = postorder_coord_subtree(subtree, prev_coord);
1043 coord != NULL;
1044 coord = postorder_coord_subtree(subtree, coord)) {
1045 if(!(prev_coord->flags & COF_FREE)) {
1046 r = _rdman_coord_free_members(rdman, prev_coord);
1047 if(r != OK)
1048 return ERR;
1049
1050 r = rdman_coord_free(rdman, prev_coord);
1051 if(r != OK)
1052 return ERR;
1053 }
1054 prev_coord = coord;
1055 }
1056 if(!(prev_coord->flags & COF_FREE)) {
1057 r = _rdman_coord_free_members(rdman, prev_coord);
1058 if(r != OK)
1059 return ERR;
1060
1061 r = rdman_coord_free(rdman, prev_coord);
1062 if(r != OK)
1063 return ERR;
1064 }
1065
1066 return OK;
1067 }
1068
1069 /*! \brief Mark a coord is changed.
1070 *
1071 * A changed coord_t object is marked as dirty and put
1072 * into dirty_coords list. rdman_coord_changed() should be called
1073 * for a coord after it been changed to notify redraw manager to
1074 * redraw shapes grouped by it.
1075 */
1076 int rdman_coord_changed(redraw_man_t *rdman, coord_t *coord) {
1077 coord_t *child;
1078
1079 if(coord->flags & COF_DIRTY)
1080 return OK;
1081
1082 add_dirty_coord(rdman, coord);
1083
1084 #if 0
1085 if(coord->flags & COF_HIDDEN)
1086 return OK;
1087 #endif
1088
1089 /* Make child coords dirty. */
1090 for(child = preorder_coord_subtree(coord, coord);
1091 child != NULL;
1092 child = preorder_coord_subtree(coord, child)) {
1093 if(child->flags & (COF_DIRTY | COF_HIDDEN)) {
1094 preorder_coord_skip_subtree(child);
1095 continue;
1096 }
1097
1098 add_dirty_coord(rdman, child);
1099
1100 if(child->flags & COF_OWN_CANVAS) {
1101 preorder_coord_skip_subtree(child);
1102 continue;
1103 }
1104 }
1105
1106 return OK;
1107 }
1108
1109 static int _rdman_shape_changed(redraw_man_t *rdman, shape_t *shape) {
1110 geo_t *geo;
1111 int r;
1112
1113 geo = shape->geo;
1114
1115 if(geo->flags & GEF_DIRTY)
1116 return OK;
1117
1118 r = add_dirty_geo(rdman, geo);
1119 if(r == ERR)
1120 return ERR;
1121
1122 return OK;
1123 }
1124
1125 /*! \brief Mark a shape is changed.
1126 *
1127 * The geo_t object of a changed shape is mark as dirty and
1128 * put into dirty_geos list.
1129 */
1130 int rdman_shape_changed(redraw_man_t *rdman, shape_t *shape) {
1131 return _rdman_shape_changed(rdman, shape);
1132 }
1133
1134 int rdman_paint_changed(redraw_man_t *rdman, paint_t *paint) {
1135 shnode_t *shnode;
1136 int r;
1137
1138 FORPAINTMEMBERS(paint, shnode) {
1139 r = _rdman_shape_changed(rdman, shnode->shape);
1140 if(r != OK)
1141 return ERR;
1142 }
1143 return OK;
1144 }
1145
1146
1147 /* Clean dirties */
1148
1149 static int is_coord_subtree_hidden(coord_t *coord) {
1150 while(coord) {
1151 if(coord->flags & COF_HIDDEN)
1152 return 1;
1153 coord = coord->parent;
1154 }
1155 return 0;
1156 }
1157
1158 static void clean_shape(shape_t *shape) {
1159 switch(MBO_TYPE(shape)) {
1160 case MBO_PATH:
1161 sh_path_transform(shape);
1162 break;
1163 case MBO_TEXT:
1164 sh_text_transform(shape);
1165 break;
1166 case MBO_RECT:
1167 sh_rect_transform(shape);
1168 break;
1169 case MBO_IMAGE:
1170 sh_image_transform(shape);
1171 break;
1172 #ifdef UNITTEST
1173 default:
1174 sh_dummy_transform(shape);
1175 break;
1176 #endif /* UNITTEST */
1177 }
1178 shape->geo->flags &= ~GEF_DIRTY;
1179
1180 if(is_coord_subtree_hidden(shape->coord))
1181 sh_hide(shape);
1182 else
1183 sh_show(shape);
1184 }
1185
1186 /*! \brief Setup canvas_info for the coord.
1187 *
1188 * Own a canvas or inherit it from parent.
1189 * \sa
1190 * - \ref redraw
1191 */
1192 static void setup_canvas_info(redraw_man_t *rdman, coord_t *coord) {
1193 if(coord->parent == NULL)
1194 return;
1195
1196 if(coord->opacity != 1 || coord_is_cached(coord)) {
1197 if(!(coord->flags & COF_OWN_CANVAS)) {
1198 /* canvas is assigned latter, in zeroing_coord() */
1199 coord->canvas_info = coord_canvas_info_new(rdman, coord, NULL);
1200 coord->flags |= COF_OWN_CANVAS;
1201 }
1202 } else {
1203 if(coord->flags & COF_OWN_CANVAS) {
1204 canvas_free(_coord_get_canvas(coord));
1205 coord_canvas_info_free(rdman, coord->canvas_info);
1206 coord->flags &= ~COF_OWN_CANVAS;
1207 }
1208 /* This must here to keep coords that do not own canvas
1209 * can always point to right canvas_info. Since, they
1210 * don't know when will parent change it's canvas_info.
1211 */
1212 coord->canvas_info = coord->parent->canvas_info;
1213 }
1214 }
1215
1216 static int coord_clean_members_n_compute_area(coord_t *coord) {
1217 geo_t *geo;
1218 /*! \note poses is shared by invokings, it is not support reentrying. */
1219 static co_aix (*poses)[2];
1220 static int max_poses = 0;
1221 int cnt, pos_cnt;
1222
1223 /* Clean member shapes. */
1224 cnt = 0;
1225 FORMEMBERS(coord, geo) {
1226 clean_shape(geo->shape);
1227 cnt++;
1228 }
1229
1230 if(max_poses < (cnt * 2)) {
1231 free(poses);
1232 max_poses = cnt * 2;
1233 poses = (co_aix (*)[2])malloc(sizeof(co_aix [2]) * max_poses);
1234 if(poses == NULL)
1235 return ERR;
1236 }
1237
1238 /* Compute area of the coord. */
1239 pos_cnt = 0;
1240 FORMEMBERS(coord, geo) {
1241 area_to_positions(geo->cur_area, poses + pos_cnt);
1242 pos_cnt += 2;
1243 }
1244
1245 area_init(coord->cur_area, pos_cnt, poses);
1246
1247 return OK;
1248 }
1249
1250 /*! \brief Clean dirty coords.
1251 *
1252 * \note coords their opacity != 1 are also traded as cached ones.
1253 */
1254 static int clean_coord(redraw_man_t *rdman, coord_t *coord) {
1255 int r;
1256
1257 setup_canvas_info(rdman, coord);
1258
1259 if(coord->flags & COF_OWN_CANVAS)
1260 compute_aggr_of_cached_coord(coord);
1261 else
1262 compute_aggr_of_coord(coord);
1263
1264 /* Areas of cached coords are computed in two phase.
1265 * Phase 1 works like other normal ones. Phase 2, is collect
1266 * all areas of descendants to compute a minimum covering area.
1267 * Phase 2 is performed by zeroing_coord().
1268 */
1269 r = coord_clean_members_n_compute_area(coord);
1270 if(r != OK)
1271 return ERR;
1272
1273 coord->flags &= ~COF_DIRTY;
1274
1275 return OK;
1276 }
1277
1278 /*! \brief Clean coord_t objects.
1279 */
1280 static int clean_rdman_coords(redraw_man_t *rdman) {
1281 coord_t *coord;
1282 coord_t **dirty_coords;
1283 int n_dirty_coords;
1284 int i, r;
1285
1286 n_dirty_coords = rdman->dirty_coords.num;
1287 if(n_dirty_coords > 0) {
1288 dirty_coords = rdman->dirty_coords.ds;
1289 _insert_sort((void **)dirty_coords, n_dirty_coords,
1290 OFFSET(coord_t, order));
1291 for(i = 0; i < n_dirty_coords; i++) {
1292 coord = dirty_coords[i];
1293 if(!(coord->flags & COF_DIRTY))
1294 continue;
1295 r = clean_coord(rdman, coord);
1296 if(r != OK)
1297 return ERR;
1298 /* These two steps can be avoided for drawing all. */
1299 add_dirty_area(rdman, coord, &coord->areas[0]);
1300 add_dirty_area(rdman, coord, &coord->areas[1]);
1301 }
1302 }
1303 return OK;
1304 }
1305
1306 static int clean_rdman_geos(redraw_man_t *rdman) {
1307 int i;
1308 int n_dirty_geos;
1309 geo_t **dirty_geos;
1310 geo_t *visit_geo;
1311 coord_t *coord;
1312
1313 n_dirty_geos = rdman->dirty_geos.num;
1314 if(n_dirty_geos > 0) {
1315 dirty_geos = rdman->dirty_geos.ds;
1316 for(i = 0; i < n_dirty_geos; i++) {
1317 visit_geo = dirty_geos[i];
1318 if(!(visit_geo->flags & GEF_DIRTY))
1319 continue;
1320
1321 clean_shape(visit_geo->shape);
1322 coord = geo_get_coord(visit_geo);
1323 add_dirty_area(rdman, coord, visit_geo->cur_area);
1324 add_dirty_area(rdman, coord, visit_geo->last_area);
1325 }
1326 }
1327
1328 return OK;
1329 }
1330
1331 /*! \brief Shift space of coord to align left-top of minimum covering.
1332 *
1333 * Align left-top of minimum rectangle covering occupied area of
1334 * sub-graphic to origin of the space.
1335 */
1336 static
1337 void zeroing_coord(redraw_man_t *rdman, coord_t *coord) {
1338 coord_t *cur;
1339 area_t *area;
1340 co_aix min_x, min_y;
1341 co_aix max_x, max_y;
1342 co_aix x, y;
1343 int w, h;
1344 int c_w, c_h;
1345 cairo_t *canvas;
1346 co_aix *aggr;
1347 co_aix poses[2][2];
1348
1349 if(coord->parent == NULL) /*! \note Should not zeroing root coord */
1350 abort();
1351 if(!(coord_is_zeroing(coord)))
1352 abort();
1353
1354 coord_clear_zeroing(coord);
1355
1356 /*
1357 * Compute minimum overing area of sub-graphic
1358 */
1359 area = &coord->canvas_info->owner_mems_area;
1360 min_x = area->x;
1361 min_y = area->y;
1362 max_x = min_x + area->w;
1363 max_y = min_y + area->h;
1364
1365 for(cur = preorder_coord_subtree(coord, coord);
1366 cur != NULL;
1367 cur = preorder_coord_subtree(coord, cur)) {
1368 area = coord_get_area(cur);
1369 if(area->x < min_x)
1370 min_x = area->x;
1371 if(area->y < min_y)
1372 min_y = area->y;
1373
1374 x = area->x + area->w;
1375 y = area->y + area->h;
1376
1377 if(x > max_x)
1378 max_x = x;
1379 if(y > max_y)
1380 max_y = y;
1381 if(cur->flags & COF_OWN_CANVAS)
1382 preorder_coord_skip_subtree(cur);
1383 }
1384
1385 w = max_x - min_x;
1386 h = max_y - min_y;
1387
1388 /*
1389 * Setup area of the coord
1390 */
1391 aggr = coord_get_aggr_matrix(coord);
1392 x = y = 0;
1393 coord_trans_pos(coord->parent, &x, &y);
1394 poses[0][0] = x;
1395 poses[0][1] = y;
1396 x = w;
1397 y = h;
1398 coord_trans_pos(coord->parent, &x, &y);
1399 poses[1][0] = x;
1400 poses[1][1] = y;
1401
1402 area_init(coord_get_area(coord), 2, poses);
1403
1404 canvas = _coord_get_canvas(coord);
1405 if(canvas)
1406 canvas_get_size(canvas, &c_w, &c_h);
1407 else
1408 c_w = c_h = 0;
1409
1410 if(!coord_get_flags(coord, COF_JUST_CLEAN) &&
1411 min_x >= 0 && min_y >= 0 && max_x <= c_w && max_y <= c_h)
1412 /* Canvas fully cover sub-graphic. */
1413 return;
1414
1415 /*
1416 * Adjust matrics of descendants to align left-top corner of
1417 * minimum covering area with origin of space defined by
1418 * zeroing coord.
1419 */
1420 FOR_COORDS_PREORDER(coord, cur) {
1421 aggr = coord_get_aggr_matrix(cur);
1422 aggr[3] -= min_x;
1423 aggr[5] -= min_y;
1424 if(coord_get_flags(cur, COF_OWN_CANVAS)) {
1425 /*
1426 * Coords, zeroing, is zeroed in preorder of tree.
1427 * So, they are zeroed after ancesters with correctly
1428 * coord_t::aggr_matrix of parent coord to zeroing.
1429 */
1430 preorder_coord_skip_subtree(cur);
1431 area = coord_get_area(cur);
1432 area->x -= min_x;
1433 area->y -= min_y;
1434 } else
1435 coord_clean_members_n_compute_area(cur);
1436 }
1437
1438 /*
1439 * Setup canvas
1440 */
1441 if(canvas == NULL || w > c_w || h > c_w) {
1442 if(canvas)
1443 canvas_free(canvas);
1444 canvas = canvas_new(w, h);
1445 _coord_set_canvas(coord, canvas);
1446 }
1447
1448 area = &coord->canvas_info->cached_dirty_area;
1449 area->x = 0;
1450 area->y = 0;
1451 area->w = w;
1452 area->h = h;
1453 DARRAY_CLEAN(_coord_get_dirty_areas(coord));
1454 add_dirty_area(rdman, coord, area);
1455 }
1456
1457 /*! \brief Add canvas owner of dirty geos to coord_t::zeroing_coords.
1458 *
1459 * All possible coords that need a zeroing have at least one dirty geo.
1460 */
1461 static int add_rdman_zeroing_coords(redraw_man_t *rdman) {
1462 int i;
1463 int n_dirty_geos;
1464 geo_t **dirty_geos, *geo;
1465 int n_dirty_coords;
1466 coord_t **dirty_coords, *coord;
1467
1468 n_dirty_geos = rdman->dirty_geos.num;
1469 dirty_geos = rdman->dirty_geos.ds;
1470 for(i = 0; i < n_dirty_geos; i++) {
1471 geo = dirty_geos[i];
1472 coord = geo_get_coord(geo)->canvas_info->owner;
1473 while(!coord_get_flags(coord, COF_MUST_ZEROING | COF_TEMP_MARK)) {
1474 coord_set_flags(coord, COF_TEMP_MARK);
1475 if(coord_is_root(coord))
1476 break;
1477 coord = coord->parent->canvas_info->owner;
1478 }
1479 }
1480
1481 n_dirty_coords = rdman->dirty_coords.num;
1482 dirty_coords = rdman->dirty_coords.ds;
1483 for(i = 0; i < n_dirty_coords; i++) {
1484 coord = dirty_coords[i]->canvas_info->owner;
1485 while(!coord_get_flags(coord, COF_MUST_ZEROING | COF_TEMP_MARK)) {
1486 coord_set_flags(coord, COF_TEMP_MARK);
1487 if(coord_is_root(coord))
1488 break;
1489 coord = coord->parent->canvas_info->owner;
1490 }
1491 }
1492
1493 FOR_COORDS_PREORDER(rdman->root_coord, coord) {
1494 if(!coord_get_flags(coord, COF_TEMP_MARK)) {
1495 preorder_coord_skip_subtree(coord);
1496 continue;
1497 }
1498 add_zeroing_coord(rdman, coord);
1499 coord_clear_flags(coord, COF_TEMP_MARK);
1500 }
1501
1502 return OK;
1503 }
1504
1505 /*! \brief Zeroing coords in redraw_man_t::zeroing_coords.
1506 *
1507 * \note redraw_man_t::zeroing_coords must in ascent partial order of tree.
1508 */
1509 static int zeroing_rdman_coords(redraw_man_t *rdman) {
1510 int i;
1511 coords_t *all_zeroing;
1512 coord_t *coord;
1513
1514 all_zeroing = &rdman->zeroing_coords;
1515 for(i = all_zeroing->num - 1; i >= 0; i--) {
1516 coord = all_zeroing->ds[i];
1517 if(coord_is_root(coord))
1518 continue;
1519 zeroing_coord(rdman, coord);
1520 }
1521
1522 return OK;
1523 }
1524
1525 /* \brief Compute matrix from cached canvas to parent device space.
1526 */
1527 static void compute_cached_2_pdev_matrix(coord_t *coord,
1528 co_aix canvas2pdev_matrix[6]) {
1529 coord_t *parent;
1530 co_aix *aggr;
1531 co_aix *matrix, *paggr;
1532 co_aix scale_x, scale_y;
1533 co_aix shift_x, shift_y;
1534 co_aix canvas2p[6];
1535
1536 aggr = coord_get_aggr_matrix(coord);
1537 matrix = coord->matrix;
1538 parent = coord->parent;
1539 paggr = coord_get_aggr_matrix(parent);
1540
1541 scale_x = matrix[0] / aggr[0];
1542 scale_y = matrix[3] / aggr[3];
1543 shift_x = matrix[2] - scale_x * aggr[2];
1544 shift_y = matrix[5] - scale_y * aggr[5];
1545
1546 canvas2p[0] = scale_x;
1547 canvas2p[1] = 0;
1548 canvas2p[2] = shift_x;
1549 canvas2p[3] = 0;
1550 canvas2p[4] = scale_y;
1551 canvas2p[5] = shift_y;
1552
1553 matrix_mul(paggr, canvas2p, canvas2pdev_matrix);
1554 }
1555
1556 /*! \brief Add aggregated dirty areas to ancestor.
1557 *
1558 * Dirty areas are aggregated into two areas. It assumes that even or odd
1559 * ones are old areas or new areas repsective. So, all even ones are
1560 * aggregated in an area, and odd ones are in another.
1561 */
1562 static void add_aggr_dirty_areas_to_ancestor(redraw_man_t *rdman,
1563 coord_t *coord) {
1564 int i;
1565 int n_areas;
1566 co_aix poses0[2][2], poses1[2][2];
1567 co_aix reverse[6];
1568 co_aix canvas2pdev_matrix[6];
1569 area_t **areas, *area;
1570 area_t *area0, *area1;
1571 coord_t *parent, *pcached_coord;
1572
1573 n_areas = _coord_get_dirty_areas(coord)->num;
1574 areas = _coord_get_dirty_areas(coord)->ds;
1575 if(n_areas == 0)
1576 abort(); /* should not happen! */
1577
1578 area0 = coord->canvas_info->aggr_dirty_areas;
1579 area1 = area0 + 1;
1580
1581 for(i = 0; i < n_areas; i++) {
1582 area = areas[i];
1583 if(area->w != 0 || area->h != 0)
1584 break;
1585 }
1586
1587 if(i < n_areas) {
1588 area = areas[i++];
1589 poses0[0][0] = area->x;
1590 poses0[0][1] = area->y;
1591 poses0[1][0] = area->x + area->w;
1592 poses0[1][1] = area->y + area->h;
1593 } else {
1594 poses0[0][0] = 0;
1595 poses0[0][1] = 0;
1596 poses0[1][0] = 0;
1597 poses0[1][1] = 0;
1598 }
1599
1600 if(i < n_areas) {
1601 area = areas[i++];
1602 poses1[0][0] = area->x;
1603 poses1[0][1] = area->y;
1604 poses1[1][0] = area->x + area->w;
1605 poses1[1][1] = area->y + area->h;
1606 } else {
1607 poses1[0][0] = 0;
1608 poses1[0][1] = 0;
1609 poses1[1][0] = 0;
1610 poses1[1][1] = 0;
1611 }
1612
1613 for(; i < n_areas - 1;) {
1614 /* Even areas */
1615 area = areas[i++];
1616 if(area->w != 0 || area->h != 0) {
1617 poses0[0][0] = MIN(poses0[0][0], area->x);
1618 poses0[0][1] = MIN(poses0[0][1], area->y);
1619 poses0[1][0] = MAX(poses0[1][0], area->x + area->w);
1620 poses0[1][1] = MAX(poses0[1][1], area->y + area->h);
1621 }
1622 /* Odd areas */
1623 area = areas[i++];
1624 if(area->w != 0 || area->h != 0) {
1625 poses1[0][0] = MIN(poses1[0][0], area->x);
1626 poses1[0][1] = MIN(poses1[0][1], area->y);
1627 poses1[1][0] = MAX(poses1[1][0], area->x + area->w);
1628 poses1[1][1] = MAX(poses1[1][1], area->y + area->h);
1629 }
1630 }
1631
1632 if(i < n_areas) {
1633 area = areas[i];
1634 if(area->w != 0 || area->h != 0) {
1635 poses0[0][0] = MIN(poses0[0][0], area->x);
1636 poses0[0][1] = MIN(poses0[0][1], area->y);
1637 poses0[1][0] = MAX(poses0[1][0], area->x + area->w);
1638 poses0[1][1] = MAX(poses0[1][1], area->y + area->h);
1639 }
1640 }
1641
1642 parent = coord->parent;
1643 pcached_coord = parent->canvas_info->owner;
1644
1645 compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
1646
1647 matrix_trans_pos(canvas2pdev_matrix, poses0[0], poses0[0] + 1);
1648 matrix_trans_pos(canvas2pdev_matrix, poses0[1], poses0[1] + 1);
1649 area_init(area0, 2, poses0);
1650 add_dirty_area(rdman, pcached_coord, area0);
1651
1652 matrix_trans_pos(canvas2pdev_matrix, poses1[0], poses1[0] + 1);
1653 matrix_trans_pos(canvas2pdev_matrix, poses1[1], poses1[1] + 1);
1654 area_init(area1, 2, poses1);
1655 if(area1->w != 0 || area1->h != 0)
1656 if(area0->x != area1->x || area0->y != area1->y ||
1657 area0->w != area1->w || area0->h != area1->h)
1658 add_dirty_area(rdman, pcached_coord, area1);
1659
1660 if(coord_get_flags(coord, COF_JUST_CLEAN) &&
1661 !coord_get_flags(pcached_coord, COF_JUST_CLEAN))
1662 add_dirty_area(rdman, pcached_coord, coord->last_area);
1663 }
1664
1665 static int add_rdman_aggr_dirty_areas(redraw_man_t *rdman) {
1666 int i;
1667 int n_zeroing;
1668 coord_t **zeroings;
1669 coord_t *coord;
1670
1671 n_zeroing = rdman->zeroing_coords.num;
1672 zeroings = rdman->zeroing_coords.ds;
1673 for(i = n_zeroing - 1; i >= 0; i--) {
1674 coord = zeroings[i];
1675 if(!coord_is_root(coord))
1676 add_aggr_dirty_areas_to_ancestor(rdman, coord);
1677 }
1678
1679 return OK;
1680 }
1681
1682 static int add_rdman_cached_dirty_areas(redraw_man_t *rdman) {
1683 int i;
1684 coord_t *coord, **dirty_coords;
1685 int n_dirty_coords;
1686
1687 n_dirty_coords = rdman->dirty_coords.num;
1688 dirty_coords = rdman->dirty_coords.ds;
1689 for(i = 0; i < n_dirty_coords; i++) {
1690 coord = dirty_coords[i];
1691 if(coord_get_flags(coord, COF_OWN_CANVAS)) {
1692 add_dirty_area(rdman, coord, coord->cur_area);
1693 add_dirty_area(rdman, coord, coord->last_area);
1694 }
1695 }
1696
1697 return OK;
1698 }
1699
1700 static int clean_rdman_dirties(redraw_man_t *rdman) {
1701 int r;
1702 int i;
1703 coord_t **coords, *coord;
1704 geo_t **geos;
1705
1706 /* coord_t::cur_area of coords are temporary pointed to
1707 * coord_canvas_info_t::owner_mems_area for store area
1708 * by clean_coor().
1709 */
1710 coords = rdman->dirty_coords.ds;
1711 for(i = 0; i < rdman->dirty_coords.num; i++) {
1712 coord = coords[i];
1713 if(coord->flags & COF_DIRTY) {
1714 if(!coord_get_flags(coord, COF_OWN_CANVAS))
1715 SWAP(coord->cur_area, coord->last_area, area_t *);
1716 else {
1717 coord->last_area = coord->cur_area;
1718 coord->cur_area = &coord->canvas_info->owner_mems_area;
1719 }
1720 }
1721 }
1722
1723 geos = rdman->dirty_geos.ds;
1724 for(i = 0; i < rdman->dirty_geos.num; i++)
1725 if(geos[i]->flags & GEF_DIRTY)
1726 SWAP(geos[i]->cur_area, geos[i]->last_area, area_t *);
1727
1728 r = clean_rdman_coords(rdman);
1729 if(r != OK)
1730 return ERR;
1731
1732 coords = rdman->dirty_coords.ds;
1733 for(i = 0; i < rdman->dirty_coords.num; i++) {
1734 coord = coords[i];
1735 coord_set_flags(coord, COF_JUST_CLEAN);
1736 coord->cur_area =
1737 (coord->last_area == coord->areas)?
1738 coord->areas + 1: coord->areas;
1739 }
1740
1741 r = clean_rdman_geos(rdman);
1742 if(r != OK)
1743 return ERR;
1744
1745 r = add_rdman_zeroing_coords(rdman);
1746 if(r != OK)
1747 return ERR;
1748
1749 r = zeroing_rdman_coords(rdman);
1750 if(r != OK)
1751 return ERR;
1752
1753 r = add_rdman_aggr_dirty_areas(rdman);
1754 if(r != OK)
1755 return ERR;
1756
1757 r = add_rdman_cached_dirty_areas(rdman);
1758 if(r != OK)
1759 return ERR;
1760
1761 coords = rdman->dirty_coords.ds;
1762 for(i = 0; i < rdman->dirty_coords.num; i++)
1763 coord_clear_flags(coords[i], COF_JUST_CLEAN);
1764
1765 return OK;
1766 }
1767
1768
1769 /* Drawing and Redrawing
1770 * ============================================================
1771 */
1772
1773 #ifndef UNITTEST
1774 static void set_shape_stroke_param(shape_t *shape, cairo_t *cr) {
1775 cairo_set_line_width(cr, shape->stroke_width);
1776 }
1777
1778 static void fill_path_preserve(redraw_man_t *rdman) {
1779 cairo_fill_preserve(rdman->cr);
1780 }
1781
1782 static void fill_path(redraw_man_t *rdman) {
1783 cairo_fill(rdman->cr);
1784 }
1785
1786 static void stroke_path(redraw_man_t *rdman) {
1787 cairo_stroke(rdman->cr);
1788 }
1789 #else
1790 static void set_shape_stroke_param(shape_t *shape, cairo_t *cr) {
1791 }
1792
1793 static void fill_path_preserve(redraw_man_t *rdman) {
1794 }
1795
1796 static void fill_path(redraw_man_t *rdman) {
1797 }
1798
1799 static void stroke_path(redraw_man_t *rdman) {
1800 }
1801 #endif
1802
1803 static void draw_shape(redraw_man_t *rdman, cairo_t *cr, shape_t *shape) {
1804 paint_t *fill, *stroke;
1805
1806 /*! \todo Move operator of shapes into singleton structures that define
1807 * operators for them.
1808 */
1809 if(shape->fill || shape->stroke) {
1810 switch(MBO_TYPE(shape)) {
1811 case MBO_PATH:
1812 sh_path_draw(shape, cr);
1813 break;
1814 case MBO_TEXT:
1815 sh_text_draw(shape, cr);
1816 break;
1817 case MBO_RECT:
1818 sh_rect_draw(shape, cr);
1819 break;
1820 case MBO_IMAGE:
1821 sh_image_draw(shape, cr);
1822 break;
1823 #ifdef UNITTEST
1824 default:
1825 sh_dummy_fill(shape, cr);
1826 break;
1827 #endif /* UNITTEST */
1828 }
1829
1830 fill = shape->fill;
1831 if(shape->fill) {
1832 fill->prepare(fill, cr);
1833 if(shape->stroke)
1834 fill_path_preserve(rdman);
1835 else
1836 fill_path(rdman);
1837 }
1838
1839 stroke = shape->stroke;
1840 if(stroke) {
1841 stroke->prepare(stroke, cr);
1842 set_shape_stroke_param(shape, cr);
1843 stroke_path(rdman);
1844 }
1845 }
1846 }
1847
1848 #ifndef UNITTEST
1849 static void clear_canvas(canvas_t *canvas) {
1850 cairo_operator_t old_op;
1851
1852 old_op = cairo_get_operator(canvas);
1853 cairo_set_operator(canvas, CAIRO_OPERATOR_CLEAR);
1854 cairo_paint(canvas);
1855 cairo_set_operator(canvas, old_op);
1856 }
1857
1858 static void make_clip(cairo_t *cr, int n_dirty_areas,
1859 area_t **dirty_areas) {
1860 int i;
1861 area_t *area;
1862
1863 cairo_new_path(cr);
1864 for(i = 0; i < n_dirty_areas; i++) {
1865 area = dirty_areas[i];
1866 cairo_rectangle(cr, area->x, area->y, area->w, area->h);
1867 }
1868 cairo_clip(cr);
1869 }
1870
1871 static void reset_clip(canvas_t *cr) {
1872 cairo_reset_clip(cr);
1873 }
1874
1875 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
1876 area_t **dirty_areas) {
1877 cairo_operator_t saved_op;
1878
1879 if(n_dirty_areas)
1880 make_clip(rdman->backend, n_dirty_areas, dirty_areas);
1881
1882 saved_op = cairo_get_operator(rdman->backend);
1883 cairo_set_operator(rdman->backend, CAIRO_OPERATOR_SOURCE);
1884 cairo_paint(rdman->backend);
1885 cairo_set_operator(rdman->backend, saved_op);
1886 }
1887 #else /* UNITTEST */
1888 static void clear_canvas(canvas_t *canvas) {
1889 }
1890
1891 static void reset_clip(canvas_t *cr) {
1892 }
1893
1894 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
1895 area_t **dirty_areas) {
1896 }
1897 #endif /* UNITTEST */
1898
1899 static void update_cached_canvas_2_parent(redraw_man_t *rdman,
1900 coord_t *coord) {
1901 cairo_t *pcanvas, *canvas;
1902 cairo_surface_t *surface;
1903 cairo_pattern_t *pattern;
1904 cairo_matrix_t cr_matrix;
1905 co_aix reverse[6];
1906 co_aix canvas2pdev_matrix[6];
1907
1908 if(coord_is_root(coord))
1909 return;
1910
1911 compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
1912 compute_reverse(canvas2pdev_matrix, reverse);
1913
1914 cr_matrix.xx = reverse[0];
1915 cr_matrix.xy = reverse[1];
1916 cr_matrix.x0 = reverse[2];
1917 cr_matrix.yx = reverse[3];
1918 cr_matrix.yy = reverse[4];
1919 cr_matrix.y0 = reverse[5];
1920
1921 canvas = _coord_get_canvas(coord);
1922 pcanvas = _coord_get_canvas(coord->parent);
1923 surface = cairo_get_target(canvas);
1924 pattern = cairo_pattern_create_for_surface(surface);
1925 cairo_pattern_set_matrix(pattern, &cr_matrix);
1926 cairo_set_source(pcanvas, pattern);
1927 cairo_paint_with_alpha(pcanvas, coord->opacity);
1928 }
1929
1930 static int draw_coord_shapes_in_dirty_areas(redraw_man_t *rdman,
1931 coord_t *coord) {
1932 int dirty = 0;
1933 int r;
1934 area_t **areas;
1935 int n_areas;
1936 cairo_t *canvas;
1937 geo_t *member;
1938 coord_t *child;
1939 int mem_idx;
1940
1941 if(coord->flags & COF_HIDDEN)
1942 return OK;
1943
1944 areas = _coord_get_dirty_areas(coord)->ds;
1945 n_areas = _coord_get_dirty_areas(coord)->num;
1946 canvas = _coord_get_canvas(coord);
1947
1948 member = FIRST_MEMBER(coord);
1949 mem_idx = 0;
1950 child = FIRST_CHILD(coord);
1951 while(child != NULL || member != NULL) {
1952 if(child && child->before_pmem == mem_idx) {
1953 if(child->flags & COF_OWN_CANVAS) {
1954 if(!(child->flags & COF_HIDDEN) &&
1955 is_area_in_areas(coord_get_area(child), n_areas, areas)) {
1956 update_cached_canvas_2_parent(rdman, child);
1957 dirty = 1;
1958 }
1959 } else {
1960 r = draw_coord_shapes_in_dirty_areas(rdman, child);
1961 dirty |= r;
1962 }
1963 child = NEXT_CHILD(child);
1964 } else {
1965 ASSERT(member != NULL);
1966 if((!(member->flags & GEF_HIDDEN)) &&
1967 is_geo_in_areas(member, n_areas, areas)) {
1968 draw_shape(rdman, canvas, member->shape);
1969 dirty = 1;
1970 }
1971
1972 member = NEXT_MEMBER(member);
1973 mem_idx++;
1974 }
1975 }
1976
1977 return dirty;
1978 }
1979
1980 static int draw_dirty_cached_coord(redraw_man_t *rdman,
1981 coord_t *coord) {
1982 area_t **areas, *area;
1983 int n_areas;
1984 cairo_t *canvas;
1985 int i;
1986 int r;
1987
1988 areas = _coord_get_dirty_areas(coord)->ds;
1989 n_areas = _coord_get_dirty_areas(coord)->num;
1990
1991 for(i = 0; i < n_areas; i++) {
1992 area = areas[i];
1993 area->x = floorf(area->x);
1994 area->y = floorf(area->y);
1995 area->w = ceilf(area->w);
1996 area->h = ceilf(area->h);
1997 }
1998
1999 canvas = _coord_get_canvas(coord);
2000 make_clip(canvas, n_areas, areas);
2001 clear_canvas(canvas);
2002
2003 r = draw_coord_shapes_in_dirty_areas(rdman, coord);
2004
2005 reset_clip(canvas);
2006 }
2007
2008 static void draw_shapes_in_dirty_areas(redraw_man_t *rdman) {
2009 int i;
2010 coord_t *coord;
2011
2012 for(i = rdman->zeroing_coords.num - 1; i >= 0; i--) {
2013 coord = rdman->zeroing_coords.ds[i];
2014 draw_dirty_cached_coord(rdman, coord);
2015 }
2016 }
2017
2018
2019 /*! \brief Re-draw all changed shapes or shapes affected by changed coords.
2020 *
2021 * A coord object has a geo to keep track the range that it's members will
2022 * draw on. Geo of a coord should be recomputed when the coord is changed.
2023 * Geo of a coord used to accelerate finding overlay shape objects of
2024 * a specified geo. A coord object also must be recomputed when one of
2025 * it's members is changed.
2026 *
2027 * New and old geo values of a coord object that is recomputed for
2028 * changing of it-self must be used to find overlay shape objects.
2029 * New and old geo values of a shape should also be used to find
2030 * overlay shape objects, too. If a shape's coord is changed, shape's
2031 * geo object is not used to find overlay shape objects any more.
2032 *
2033 * steps:
2034 * - update chagned coord objects
2035 * - recompute area for changed coord objects
2036 * - recompute geo for members shape objects
2037 * - clear dirty of geo for members to prevent from
2038 * recomputing for change of shape objects.
2039 * - add old and new area value to list of dirty areas.
2040 * - recompute geo for changed shape objects
2041 * - only if a shape object is dirty.
2042 * - put new and old value of area of geo to list of dirty areas.
2043 * - Scan all shapes and redraw shapes overlaid with dirty areas.
2044 *
2045 * dirty flag of coord objects is cleared after update.
2046 * dirty flag of geo objects is also cleared after recomputing.
2047 * Clean dirty flag can prevent redundant computing for geo and
2048 * corod objects.
2049 *
2050 */
2051 int rdman_redraw_changed(redraw_man_t *rdman) {
2052 int r;
2053 event_t event;
2054 subject_t *redraw;
2055 int i;
2056 coord_t *coord, **coords;
2057 int n_areas;
2058 area_t **areas;
2059
2060 r = clean_rdman_dirties(rdman);
2061 if(r != OK)
2062 return ERR;
2063
2064 if(rdman->n_dirty_areas > 0) {
2065 /*! \brief Draw shapes in preorder of coord tree and support opacity
2066 * rules.
2067 */
2068 draw_shapes_in_dirty_areas(rdman);
2069 n_areas = _coord_get_dirty_areas(rdman->root_coord)->num;
2070 areas = _coord_get_dirty_areas(rdman->root_coord)->ds;
2071 copy_cr_2_backend(rdman, n_areas, areas);
2072 reset_clip(rdman->backend);
2073 for(i = 0; i < rdman->zeroing_coords.num; i++) {
2074 coord = rdman->zeroing_coords.ds[i];
2075 DARRAY_CLEAN(_coord_get_dirty_areas(coord));
2076 }
2077 rdman->n_dirty_areas = 0;
2078 }
2079
2080 coords = rdman->zeroing_coords.ds;
2081 for(i = 0; i < rdman->zeroing_coords.num; i++) {
2082 coord = coords[i];
2083 coord_clear_flags(coord, COF_MUST_ZEROING);
2084 }
2085
2086 DARRAY_CLEAN(&rdman->dirty_coords);
2087 DARRAY_CLEAN(&rdman->dirty_geos);
2088 DARRAY_CLEAN(&rdman->zeroing_coords);
2089
2090 /* Free postponsed removing */
2091 free_free_objs(rdman);
2092
2093 redraw = rdman_get_redraw_subject(rdman);
2094 event.type = EVT_RDMAN_REDRAW;
2095 event.tgt = event.cur_tgt = redraw;
2096 subject_notify(redraw, &event);
2097
2098 return OK;
2099 }
2100
2101 /* NOTE: Before redrawing, the canvas/surface must be cleaned.
2102 * NOTE: After redrawing, the content must be copied to the backend surface.
2103 */
2104
2047 int rdman_redraw_all(redraw_man_t *rdman) { 2105 int rdman_redraw_all(redraw_man_t *rdman) {
2048 area_t area; 2106 area_t area;
2049 #ifndef UNITTEST 2107 #ifndef UNITTEST
2050 cairo_surface_t *surface; 2108 cairo_surface_t *surface;
2051 #endif 2109 #endif
2117 r = clean_rdman_dirties(rdman); 2175 r = clean_rdman_dirties(rdman);
2118 2176
2119 return r; 2177 return r;
2120 } 2178 }
2121 2179
2122 /*! \page dirty Dirty geo, coord, and area.
2123 *
2124 * \section dirty_of_ego Dirty of geo
2125 * A geo is dirty when any of the shape, size or positions is changed.
2126 * It's geo and positions should be recomputed before drawing. So,
2127 * dirty geos are marked as dirty and put into dirty_geos list.
2128 * The list is inspected before drawing to make sure the right shape,
2129 * size, and positions.
2130 *
2131 * \section dirty_of_coord Dirty of coord
2132 * A coord is dirty when it's transformation matrix being changed.
2133 * Dirty coords are marked as dirty and put into dirty_coords list.
2134 * Once a coord is dirty, every member geos of it are also dirty.
2135 * Because, their shape, size and positions will be changed. But,
2136 * they are not marked as dirty and put into dirty_geos list, since
2137 * all these member geos will be recomputed for computing new current
2138 * area of the coord. The changes of a coord also affect child
2139 * coords. Once parent is dirty, all children are also dirty for
2140 * their aggregate matrix out of date. Dirty coords should be
2141 * clean in preorder of tree traversal. The dirty_coords list
2142 * are sorted to keep the order before cleaning.
2143 * Whenever a coord is marked dirty and put into dirty_coords list,
2144 * all it's children should also be marked and put.
2145 *
2146 * The procedure of clean coords comprises recomputing aggregate
2147 * tranform matrix and area where members spreading in.
2148 *
2149 * The list is inspected before drawing to recompute new shape, size,
2150 * and positions of member geos of coords in the list. The drity
2151 * flag of member geos will be clean.
2152 *
2153 * Clean coords should be performed before clean geos, since clean
2154 * coords will also clean member geos.
2155 */
2156
2157 /*! \page man_obj Manage Objects. 2180 /*! \page man_obj Manage Objects.
2158 * 2181 *
2159 * Shapes and paints should also be managed by redraw manager. Redraw 2182 * Shapes and paints should also be managed by redraw manager. Redraw
2160 * manager must know life-cycle of shapes and paints to avoid to use them 2183 * manager must know life-cycle of shapes and paints to avoid to use them
2161 * after being free. If a shape is released when it is dirty, redraw 2184 * after being free. If a shape is released when it is dirty, redraw
2173 * - rdman_paint_*_new() 2196 * - rdman_paint_*_new()
2174 * - rdman_paint_free() 2197 * - rdman_paint_free()
2175 * - rdman_shape_*_new() 2198 * - rdman_shape_*_new()
2176 * - rdman_shape_free() 2199 * - rdman_shape_free()
2177 */ 2200 */
2178
2179 /*
2180 * To accelerate speed of transformation, when a matrix changed,
2181 * transformation should be aggregated and computed in a loop.
2182 * It can get intereset of higher hit rate of cache.
2183 * - shapes prvoide list of positions needed to be transformed.
2184 * - redraw_man transforms positions from shapes.
2185 * - shapes drawing with result of transforms.
2186 * - shapes should be called to give them a chance to update geometries.
2187 */
2188
2189 2201
2190 /* \defgroup rdman_observer Observer memory management 2202 /* \defgroup rdman_observer Observer memory management
2191 * 2203 *
2192 * Implment factory and strategy functions for observers and subjects. 2204 * Implment factory and strategy functions for observers and subjects.
2193 * @{ 2205 * @{