Mercurial > MadButterfly
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 * @{ |