00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include <string.h>
00004 #include <cairo.h>
00005 #include "mb_types.h"
00006 #include "mb_shapes.h"
00007 #include "mb_tools.h"
00008 #include "mb_redraw_man.h"
00009 #include "mb_observer.h"
00010
00011
00012
00013
00014
00015 #define sh_attach_geo(sh, g) \
00016 do { \
00017 (sh)->geo = g; \
00018 (g)->shape = (shape_t *)(sh); \
00019 } while(0)
00020 #define sh_detach_geo(sh) \
00021 do { \
00022 (sh)->geo->shape = NULL; \
00023 (sh)->geo = NULL; \
00024 } while(0)
00025 #define sh_get_geo(sh) ((sh)->geo)
00026 #define sh_attach_coord(sh, coord) do { (sh)->coord = coord; } while(0)
00027 #define sh_detach_coord(sh) do { (sh)->coord = NULL; } while(0)
00028 #define rdman_is_dirty(rdman) \
00029 ((rdman)->dirty_coords.num != 0 || \
00030 (rdman)->dirty_geos.num != 0 || \
00031 (rdman)->dirty_areas.num != 0)
00032
00033 #define OK 0
00034 #define ERR -1
00035
00036 #define ARRAY_EXT_SZ 64
00037
00038 #define SWAP(a, b, t) do { t c; c = a; a = b; b = c; } while(0)
00039
00040 #ifdef UNITTEST
00041 typedef struct _sh_dummy sh_dummy_t;
00042
00043 extern void sh_dummy_transform(shape_t *shape);
00044 extern void sh_dummy_fill(shape_t *, cairo_t *);
00045 #endif
00046
00047 static subject_t *ob_subject_alloc(ob_factory_t *factory);
00048 static void ob_subject_free(ob_factory_t *factory, subject_t *subject);
00049 static observer_t *ob_observer_alloc(ob_factory_t *factory);
00050 static void ob_observer_free(ob_factory_t *factory, observer_t *observer);
00051 static subject_t *ob_get_parent_subject(ob_factory_t *factory,
00052 subject_t *cur_subject);
00053
00054 #define FORCHILDREN(coord, child) \
00055 for((child) = STAILQ_HEAD((coord)->children); \
00056 (child) != NULL; \
00057 (child) = STAILQ_NEXT(coord_t, sibling, (child)))
00058 #define NEXT_CHILD(child) STAILQ_NEXT(coord_t, sibling, child)
00059 #define ADD_CHILD(parent, child) \
00060 STAILQ_INS_TAIL((parent)->children, coord_t, sibling, (child))
00061 #define RM_CHILD(parent, child) \
00062 STAILQ_REMOVE((parent)->children, coord_t, sibling, (child))
00063 #define FIRST_CHILD(parent) STAILQ_HEAD((parent)->children)
00064
00065
00066 #define FORMEMBERS(coord, member) \
00067 for((member) = STAILQ_HEAD((coord)->members); \
00068 (member) != NULL; \
00069 (member) = STAILQ_NEXT(geo_t, coord_next, (member)))
00070 #define NEXT_MEMBER(member) STAILQ_NEXT(geo_t, coord_next, (member))
00071 #define ADD_MEMBER(coord, member) \
00072 STAILQ_INS_TAIL((coord)->members, geo_t, coord_next, (member))
00073 #define RM_MEMBER(coord, member) \
00074 STAILQ_REMOVE((coord)->members, geo_t, coord_next, (member))
00075 #define FIRST_MEMBER(coord) STAILQ_HEAD((coord)->members)
00076
00077
00078 #define FORPAINTMEMBERS(paint, member) \
00079 for((member) = STAILQ_HEAD((paint)->members); \
00080 (member) != NULL; \
00081 (member) = STAILQ_NEXT(paint_t, next, member))
00082 #define RM_PAINTMEMBER(paint, member) \
00083 STAILQ_REMOVE((paint)->members, shnode_t, next, member)
00084 #define RM_PAINT(rdman, paint) \
00085 STAILQ_REMOVE((rdman)->paints, paint_t, pnt_next, paint)
00086
00087
00088
00089
00090
00091
00092 static void _insert_sort(void **elms, int num, int off) {
00093 int i, j;
00094 unsigned int val;
00095 void *elm_i;
00096
00097 for(i = 1; i < num; i++) {
00098 elm_i = elms[i];
00099 val = *(unsigned int *)(elm_i + off);
00100 for(j = i; j > 0; j--) {
00101 if(*(unsigned int *)(elms[j - 1] + off) <= val)
00102 break;
00103 elms[j] = elms[j - 1];
00104 }
00105 elms[j] = elm_i;
00106 }
00107 }
00108
00109 DARRAY_DEFINE(coords, coord_t *);
00110 DARRAY_DEFINE(geos, geo_t *);
00111 DARRAY_DEFINE(areas, area_t *);
00112
00113
00114
00115 #define ADD_DATA(sttype, field, v) \
00116 int r; \
00117 r = sttype ## _add(&rdman->field, v); \
00118 return r == 0? OK: ERR;
00119
00120
00121 static int add_dirty_coord(redraw_man_t *rdman, coord_t *coord) {
00122 coord->flags |= COF_DIRTY;
00123 ADD_DATA(coords, dirty_coords, coord);
00124 }
00125
00126 static int add_dirty_geo(redraw_man_t *rdman, geo_t *geo) {
00127 geo->flags |= GEF_DIRTY;
00128 ADD_DATA(geos, dirty_geos, geo);
00129 }
00130
00131 static int add_dirty_area(redraw_man_t *rdman, area_t *area) {
00132 ADD_DATA(areas, dirty_areas, area);
00133 }
00134
00135 static int add_free_obj(redraw_man_t *rdman, void *obj,
00136 free_func_t free_func) {
00137 int max;
00138 free_obj_t *new_objs, *free_obj;
00139
00140 if(rdman->free_objs.num >= rdman->free_objs.max) {
00141 max = rdman->free_objs.num + ARRAY_EXT_SZ;
00142 new_objs = realloc(rdman->free_objs.objs,
00143 max * sizeof(free_obj_t));
00144 if(new_objs == NULL)
00145 return ERR;
00146 rdman->free_objs.max = max;
00147 rdman->free_objs.objs = new_objs;
00148 }
00149
00150 free_obj = rdman->free_objs.objs + rdman->free_objs.num++;
00151 free_obj->obj = obj;
00152 free_obj->free_func = free_func;
00153
00154 return OK;
00155 }
00156
00157 static void free_free_objs(redraw_man_t *rdman) {
00158 int i;
00159 free_obj_t *free_obj;
00160
00161 for(i = 0; i < rdman->free_objs.num; i++) {
00162 free_obj = &rdman->free_objs.objs[i];
00163 free_obj->free_func(rdman, free_obj->obj);
00164 }
00165 rdman->free_objs.num = 0;
00166 }
00167
00168 static void free_objs_destroy(redraw_man_t *rdman) {
00169 if(rdman->free_objs.objs != NULL)
00170 free(rdman->free_objs.objs);
00171 }
00172
00173 static void area_to_positions(area_t *area, co_aix (*poses)[2]) {
00174 poses[0][0] = area->x;
00175 poses[0][1] = area->y;
00176 poses[1][0] = area->x + area->w;
00177 poses[1][1] = area->y + area->h;;
00178 }
00179
00180 static cairo_t *new_canvas(redraw_man_t *rdman) {
00181 #ifndef UNITTEST
00182 cairo_t *cr;
00183 cairo_surface_t *surface, *cr_surface;
00184 int w, h;
00185
00186 cr_surface = cairo_get_target(rdman->cr);
00187 w = cairo_image_surface_get_width(cr_surface);
00188 h = cairo_image_surface_get_height(cr_surface);
00189 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
00190 w, h);
00191 cr = cairo_create(surface);
00192
00193 return cr;
00194 #else
00195 return NULL;
00196 #endif
00197 }
00198
00199 static void free_canvas(cairo_t *canvas) {
00200 #ifndef UNITTEST
00201 cairo_destroy(canvas);
00202 #endif
00203 }
00204
00205 static int geo_off_in_coord(geo_t *geo, coord_t *coord) {
00206 int off = 0;
00207 geo_t *vgeo;
00208
00209 FORMEMBERS(coord, vgeo) {
00210 if(vgeo == geo)
00211 return off;
00212 off++;
00213 }
00214 return -1;
00215 }
00216
00217 static void geo_attach_coord(geo_t *geo, coord_t *coord) {
00218 ADD_MEMBER(coord, geo);
00219 coord->num_members++;
00220 }
00221
00222 static void geo_detach_coord(geo_t *geo, coord_t *coord) {
00223 int off;
00224 coord_t *child;
00225
00226 off = geo_off_in_coord(geo, coord);
00227 if(off < 0)
00228 return;
00229 FORCHILDREN(coord, child) {
00230 if(child->before_pmem >= off)
00231 child->before_pmem--;
00232 }
00233
00234 RM_MEMBER(coord, geo);
00235 coord->num_members--;
00236 }
00237
00238 int redraw_man_init(redraw_man_t *rdman, cairo_t *cr, cairo_t *backend) {
00239 extern void redraw_man_destroy(redraw_man_t *rdman);
00240 extern int _paint_color_size;
00241
00242 memset(rdman, 0, sizeof(redraw_man_t));
00243
00244 rdman->geo_pool = elmpool_new(sizeof(geo_t), 128);
00245 if(rdman->geo_pool == NULL)
00246 return ERR;
00247
00248 rdman->coord_pool = elmpool_new(sizeof(coord_t), 16);
00249 if(rdman->coord_pool == NULL) {
00250 elmpool_free(rdman->geo_pool);
00251 return ERR;
00252 }
00253
00254 rdman->shnode_pool = elmpool_new(sizeof(shnode_t), 16);
00255 if(rdman->shnode_pool == NULL) {
00256 elmpool_free(rdman->geo_pool);
00257 elmpool_free(rdman->coord_pool);
00258 return ERR;
00259 }
00260
00261 rdman->observer_pool = elmpool_new(sizeof(observer_t), 32);
00262 if(rdman->observer_pool == NULL) {
00263 elmpool_free(rdman->geo_pool);
00264 elmpool_free(rdman->coord_pool);
00265 elmpool_free(rdman->shnode_pool);
00266 return ERR;
00267 }
00268
00269 rdman->subject_pool = elmpool_new(sizeof(subject_t), 32);
00270 if(rdman->subject_pool == NULL) {
00271 elmpool_free(rdman->geo_pool);
00272 elmpool_free(rdman->coord_pool);
00273 elmpool_free(rdman->shnode_pool);
00274 elmpool_free(rdman->observer_pool);
00275 return ERR;
00276 }
00277
00278 rdman->paint_color_pool = elmpool_new(_paint_color_size, 64);
00279 if(rdman->subject_pool == NULL) {
00280 elmpool_free(rdman->geo_pool);
00281 elmpool_free(rdman->coord_pool);
00282 elmpool_free(rdman->shnode_pool);
00283 elmpool_free(rdman->observer_pool);
00284 elmpool_free(rdman->subject_pool);
00285 return ERR;
00286 }
00287
00288 rdman->ob_factory.subject_alloc = ob_subject_alloc;
00289 rdman->ob_factory.subject_free = ob_subject_free;
00290 rdman->ob_factory.observer_alloc = ob_observer_alloc;
00291 rdman->ob_factory.observer_free = ob_observer_free;
00292 rdman->ob_factory.get_parent_subject = ob_get_parent_subject;
00293
00294 rdman->redraw =
00295 subject_new(&rdman->ob_factory, rdman, OBJT_RDMAN);
00296
00297 rdman->root_coord = elmpool_elm_alloc(rdman->coord_pool);
00298 if(rdman->root_coord == NULL)
00299 redraw_man_destroy(rdman);
00300 rdman->n_coords = 1;
00301 coord_init(rdman->root_coord, NULL);
00302 rdman->root_coord->mouse_event = subject_new(&rdman->ob_factory,
00303 rdman->root_coord,
00304 OBJT_COORD);
00305 rdman->root_coord->flags |= COF_OWN_CANVAS;
00306 rdman->root_coord->canvas = cr;
00307 rdman->root_coord->opacity = 1;
00308
00309 rdman->cr = cr;
00310 rdman->backend = backend;
00311
00312 STAILQ_INIT(rdman->shapes);
00313 STAILQ_INIT(rdman->paints);
00314
00315 return OK;
00316 }
00317
00318 void redraw_man_destroy(redraw_man_t *rdman) {
00319 coord_t *coord, *saved_coord;
00320 shape_t *shape, *saved_shape;
00321 geo_t *member;
00322
00323 free_free_objs(rdman);
00324 free_objs_destroy(rdman);
00325
00326 coord = postorder_coord_subtree(rdman->root_coord, NULL);
00327 while(coord) {
00328 saved_coord = coord;
00329 coord = postorder_coord_subtree(rdman->root_coord, coord);
00330 FORMEMBERS(saved_coord, member) {
00331 rdman_shape_free(rdman, member->shape);
00332 }
00333 rdman_coord_free(rdman, saved_coord);
00334 }
00335 FORMEMBERS(saved_coord, member) {
00336 rdman_shape_free(rdman, member->shape);
00337 }
00338
00339
00340
00341
00342 shape = saved_shape = STAILQ_HEAD(rdman->shapes);
00343 while(shape && (shape = STAILQ_NEXT(shape_t, sh_next, shape))) {
00344 rdman_shape_free(rdman, saved_shape);
00345 #if 0
00346 STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, saved_shape);
00347 #endif
00348 saved_shape = shape;
00349 }
00350 if(saved_shape != NULL)
00351 rdman_shape_free(rdman, saved_shape);
00352
00353 elmpool_free(rdman->coord_pool);
00354 elmpool_free(rdman->geo_pool);
00355 elmpool_free(rdman->shnode_pool);
00356 elmpool_free(rdman->observer_pool);
00357 elmpool_free(rdman->subject_pool);
00358 elmpool_free(rdman->paint_color_pool);
00359
00360 DARRAY_DESTROY(&rdman->dirty_coords);
00361 DARRAY_DESTROY(&rdman->dirty_geos);
00362 DARRAY_DESTROY(&rdman->dirty_areas);
00363 DARRAY_DESTROY(&rdman->gen_geos);
00364 }
00365
00366
00367 #define ASSERT(x)
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390 int rdman_add_shape(redraw_man_t *rdman, shape_t *shape, coord_t *coord) {
00391 geo_t *geo;
00392 int r;
00393
00394 geo = elmpool_elm_alloc(rdman->geo_pool);
00395 if(geo == NULL)
00396 return ERR;
00397
00398 geo_init(geo);
00399 geo->mouse_event = subject_new(&rdman->ob_factory, geo, OBJT_GEO);
00400
00401 geo_attach_coord(geo, coord);
00402
00403
00404 r = add_dirty_geo(rdman, geo);
00405 if(r != OK)
00406 return ERR;
00407
00408 sh_attach_coord(shape, coord);
00409 sh_attach_geo(shape, geo);
00410
00411 return OK;
00412 }
00413
00414
00415
00416
00417
00418
00419
00420
00421 int rdman_shape_free(redraw_man_t *rdman, shape_t *shape) {
00422 geo_t *geo;
00423 int r;
00424
00425 geo = shape->geo;
00426
00427 if(rdman_is_dirty(rdman) && geo != NULL) {
00428 if(geo->flags & GEF_FREE)
00429 return ERR;
00430
00431 geo->flags |= GEF_FREE;
00432 sh_hide(shape);
00433 if(!(geo->flags & GEF_DIRTY)) {
00434 r = add_dirty_geo(rdman, geo);
00435 if(r != OK)
00436 return ERR;
00437 }
00438 r = add_free_obj(rdman, shape, (free_func_t)rdman_shape_free);
00439 if(r != OK)
00440 return ERR;
00441 return OK;
00442 }
00443
00444 if(geo != NULL) {
00445 geo_detach_coord(geo, shape->coord);
00446 sh_detach_coord(shape);
00447 sh_detach_geo(shape);
00448 subject_free(&rdman->ob_factory, geo->mouse_event);
00449 elmpool_elm_free(rdman->geo_pool, geo);
00450 }
00451 STAILQ_REMOVE(rdman->shapes, shape_t, sh_next, shape);
00452 shape->free(shape);
00453 return OK;
00454 }
00455
00456 shnode_t *shnode_new(redraw_man_t *rdman, shape_t *shape) {
00457 shnode_t *node;
00458
00459 node = (shnode_t *)elmpool_elm_alloc(rdman->shnode_pool);
00460 if(node) {
00461 node->shape = shape;
00462 node->next = NULL;
00463 }
00464 return node;
00465 }
00466
00467 int rdman_paint_free(redraw_man_t *rdman, paint_t *paint) {
00468 shnode_t *shnode, *saved_shnode;
00469
00470 if(rdman_is_dirty(rdman)) {
00471 if(!(paint->flags & PNTF_FREE))
00472 return ERR;
00473 add_free_obj(rdman, paint, (free_func_t)rdman_paint_free);
00474 paint->flags |= PNTF_FREE;
00475 return OK;
00476 }
00477
00478
00479 saved_shnode = NULL;
00480 FORPAINTMEMBERS(paint, shnode) {
00481 if(saved_shnode) {
00482 RM_PAINTMEMBER(paint, saved_shnode);
00483 shnode_free(rdman, saved_shnode);
00484 }
00485 saved_shnode = shnode;
00486 }
00487 if(saved_shnode) {
00488 RM_PAINTMEMBER(paint, saved_shnode);
00489 shnode_free(rdman, saved_shnode);
00490 }
00491
00492 RM_PAINT(rdman, paint);
00493 paint->free(rdman, paint);
00494 return OK;
00495 }
00496
00497 void _rdman_paint_real_remove_child(redraw_man_t *rdman,
00498 paint_t *paint,
00499 shape_t *shape) {
00500 shnode_t *shnode;
00501
00502 FORPAINTMEMBERS(paint, shnode) {
00503 if(shnode->shape == shape) {
00504 RM_PAINTMEMBER(paint, shnode);
00505 shnode_free(rdman, shnode);
00506 break;
00507 }
00508 }
00509 }
00510
00511 coord_t *rdman_coord_new(redraw_man_t *rdman, coord_t *parent) {
00512 coord_t *coord, *root_coord;
00513 coord_t *visit;
00514
00515 coord = elmpool_elm_alloc(rdman->coord_pool);
00516 if(coord == NULL)
00517 return NULL;
00518
00519 coord_init(coord, parent);
00520 coord->mouse_event = subject_new(&rdman->ob_factory,
00521 coord,
00522 OBJT_COORD);
00523
00524 coord->opacity = 1;
00525 if(parent)
00526 coord->canvas = parent->canvas;
00527 rdman->n_coords++;
00528
00529 coord->order = ++rdman->next_coord_order;
00530 if(coord->order == 0) {
00531 rdman->next_coord_order = 0;
00532 root_coord = visit = rdman->root_coord;
00533
00534 visit = preorder_coord_subtree(root_coord, visit);
00535 while(visit) {
00536 visit->order = ++rdman->next_coord_order;
00537 visit = preorder_coord_subtree(root_coord, visit);
00538 }
00539 }
00540
00541 coord->before_pmem = parent->num_members;
00542
00543
00544 if(parent && (parent->flags & COF_DIRTY))
00545 add_dirty_coord(rdman, coord);
00546
00547 return coord;
00548 }
00549
00550
00551
00552
00553
00554
00555
00556
00557 int rdman_coord_free(redraw_man_t *rdman, coord_t *coord) {
00558 coord_t *parent;
00559 coord_t *child;
00560 geo_t *member;
00561 int r;
00562
00563 parent = coord->parent;
00564 if(parent == NULL)
00565 return ERR;
00566
00567 if(rdman_is_dirty(rdman)) {
00568 if(coord->flags & COF_FREE)
00569 return ERR;
00570
00571 FORCHILDREN(coord, child) {
00572 if(!(child->flags & COF_FREE))
00573 return ERR;
00574 }
00575 FORMEMBERS(coord, member) {
00576 if(!(member->flags & GEF_FREE))
00577 return ERR;
00578 }
00579 coord->flags |= COF_FREE;
00580 coord_hide(coord);
00581 if(!(coord->flags & COF_DIRTY)) {
00582 r = add_dirty_coord(rdman, coord);
00583 if(r != OK)
00584 return ERR;
00585 }
00586 r = add_free_obj(rdman, coord, (free_func_t)rdman_coord_free);
00587 if(r != OK)
00588 return ERR;
00589 return OK;
00590 }
00591
00592 if(FIRST_MEMBER(coord) != NULL)
00593 return ERR;
00594
00595 if(FIRST_CHILD(coord) != NULL)
00596 return ERR;
00597
00598
00599 if(coord->flags & COF_OWN_CANVAS)
00600 free_canvas(coord->canvas);
00601
00602 RM_CHILD(parent, coord);
00603 subject_free(&rdman->ob_factory, coord->mouse_event);
00604 elmpool_elm_free(rdman->coord_pool, coord);
00605 rdman->n_coords--;
00606
00607 return OK;
00608 }
00609
00610 int rdman_coord_subtree_free(redraw_man_t *rdman, coord_t *subtree) {
00611 coord_t *coord, *prev_coord;
00612 int r;
00613
00614 if(subtree == NULL)
00615 return OK;
00616
00617 prev_coord = postorder_coord_subtree(subtree, NULL);
00618 for(coord = postorder_coord_subtree(subtree, prev_coord);
00619 coord != NULL;
00620 coord = postorder_coord_subtree(subtree, coord)) {
00621 if(!(prev_coord->flags & COF_FREE)) {
00622 r = rdman_coord_free(rdman, prev_coord);
00623 if(r != OK)
00624 return ERR;
00625 }
00626 prev_coord = coord;
00627 }
00628 if(!(prev_coord->flags & COF_FREE)) {
00629 r = rdman_coord_free(rdman, prev_coord);
00630 if(r != OK)
00631 return ERR;
00632 }
00633
00634 return OK;
00635 }
00636
00637
00638
00639
00640
00641
00642
00643
00644 int rdman_coord_changed(redraw_man_t *rdman, coord_t *coord) {
00645 coord_t *child;
00646
00647 if(coord->flags & COF_DIRTY)
00648 return OK;
00649
00650 add_dirty_coord(rdman, coord);
00651
00652 #if 0
00653 if(coord->flags & COF_HIDDEN)
00654 return OK;
00655 #endif
00656
00657
00658 for(child = preorder_coord_subtree(coord, coord);
00659 child != NULL;
00660 child = preorder_coord_subtree(coord, child)) {
00661 if(child->flags & (COF_DIRTY | COF_HIDDEN)) {
00662 preorder_coord_skip_subtree(child);
00663 continue;
00664 }
00665
00666 add_dirty_coord(rdman, child);
00667 }
00668
00669 return OK;
00670 }
00671
00672 static int _rdman_shape_changed(redraw_man_t *rdman, shape_t *shape) {
00673 geo_t *geo;
00674 int r;
00675
00676 geo = shape->geo;
00677
00678 if(geo->flags & GEF_DIRTY)
00679 return OK;
00680
00681 r = add_dirty_geo(rdman, geo);
00682 if(r == ERR)
00683 return ERR;
00684
00685 return OK;
00686 }
00687
00688
00689
00690
00691
00692
00693 int rdman_shape_changed(redraw_man_t *rdman, shape_t *shape) {
00694 return _rdman_shape_changed(rdman, shape);
00695 }
00696
00697 int rdman_paint_changed(redraw_man_t *rdman, paint_t *paint) {
00698 shnode_t *shnode;
00699 int r;
00700
00701 FORPAINTMEMBERS(paint, shnode) {
00702 r = _rdman_shape_changed(rdman, shnode->shape);
00703 if(r != OK)
00704 return ERR;
00705 }
00706 return OK;
00707 }
00708
00709
00710
00711 static int is_coord_subtree_hidden(coord_t *coord) {
00712 while(coord) {
00713 if(coord->flags & COF_HIDDEN)
00714 return 1;
00715 coord = coord->parent;
00716 }
00717 return 0;
00718 }
00719
00720 static void clean_shape(shape_t *shape) {
00721 switch(shape->sh_type) {
00722 case SHT_PATH:
00723 sh_path_transform(shape);
00724 break;
00725 case SHT_TEXT:
00726 sh_text_transform(shape);
00727 break;
00728 case SHT_RECT:
00729 sh_rect_transform(shape);
00730 break;
00731 #ifdef UNITTEST
00732 default:
00733 sh_dummy_transform(shape);
00734 break;
00735 #endif
00736 }
00737 shape->geo->flags &= ~GEF_DIRTY;
00738
00739 if(is_coord_subtree_hidden(shape->coord))
00740 sh_hide(shape);
00741 else
00742 sh_show(shape);
00743 }
00744
00745
00746
00747
00748
00749
00750
00751 static void setup_canvas(redraw_man_t *rdman, coord_t *coord) {
00752 if(coord->parent == NULL)
00753 return;
00754
00755 if(coord->opacity != 1) {
00756 if(!(coord->flags & COF_OWN_CANVAS)) {
00757 coord->canvas = new_canvas(rdman);
00758 coord->flags |= COF_OWN_CANVAS;
00759 }
00760 } else {
00761 if(coord->flags & COF_OWN_CANVAS) {
00762 free_canvas(coord->canvas);
00763 coord->flags &= ~COF_OWN_CANVAS;
00764 }
00765 coord->canvas = coord->parent->canvas;
00766 }
00767 }
00768
00769 static int clean_coord(redraw_man_t *rdman, coord_t *coord) {
00770 geo_t *geo;
00771
00772 static co_aix (*poses)[2];
00773 static int max_poses = 0;
00774 int cnt, pos_cnt;
00775
00776 setup_canvas(rdman, coord);
00777
00778 compute_aggr_of_coord(coord);
00779
00780
00781 cnt = 0;
00782 FORMEMBERS(coord, geo) {
00783 SWAP(geo->cur_area, geo->last_area, area_t *);
00784 clean_shape(geo->shape);
00785 cnt++;
00786 }
00787
00788 if(max_poses < (cnt * 2)) {
00789 free(poses);
00790 max_poses = cnt * 2;
00791 poses = (co_aix (*)[2])malloc(sizeof(co_aix [2]) * max_poses);
00792 if(poses == NULL)
00793 return ERR;
00794 }
00795
00796
00797 pos_cnt = 0;
00798 FORMEMBERS(coord, geo) {
00799 area_to_positions(geo->cur_area, poses + pos_cnt);
00800 pos_cnt += 2;
00801 }
00802
00803 SWAP(coord->cur_area, coord->last_area, area_t *);
00804 area_init(coord->cur_area, pos_cnt, poses);
00805
00806 coord->flags &= ~COF_DIRTY;
00807
00808 return OK;
00809 }
00810
00811
00812
00813 static int clean_rdman_coords(redraw_man_t *rdman) {
00814 coord_t *coord;
00815 coord_t **dirty_coords;
00816 int n_dirty_coords;
00817 int i, r;
00818
00819 n_dirty_coords = rdman->dirty_coords.num;
00820 if(n_dirty_coords > 0) {
00821 dirty_coords = rdman->dirty_coords.ds;
00822 _insert_sort((void **)dirty_coords, n_dirty_coords,
00823 OFFSET(coord_t, order));
00824 for(i = 0; i < n_dirty_coords; i++) {
00825 coord = dirty_coords[i];
00826 if(!(coord->flags & COF_DIRTY))
00827 continue;
00828 r = clean_coord(rdman, coord);
00829 if(r != OK)
00830 return ERR;
00831
00832 add_dirty_area(rdman, &coord->areas[0]);
00833 add_dirty_area(rdman, &coord->areas[1]);
00834 }
00835 rdman->dirty_coords.num = 0;
00836 }
00837 return OK;
00838 }
00839
00840 static int clean_rdman_geos(redraw_man_t *rdman) {
00841 int i;
00842 int n_dirty_geos;
00843 geo_t **dirty_geos;
00844 geo_t *visit_geo;
00845
00846 n_dirty_geos = rdman->dirty_geos.num;
00847 if(n_dirty_geos > 0) {
00848 dirty_geos = rdman->dirty_geos.ds;
00849 for(i = 0; i < n_dirty_geos; i++) {
00850 visit_geo = dirty_geos[i];
00851 if(!(visit_geo->flags & GEF_DIRTY))
00852 continue;
00853
00854 SWAP(visit_geo->cur_area, visit_geo->last_area, area_t *);
00855 clean_shape(visit_geo->shape);
00856 add_dirty_area(rdman, visit_geo->cur_area);
00857 add_dirty_area(rdman, visit_geo->last_area);
00858 }
00859 rdman->dirty_geos.num = 0;
00860 }
00861
00862 return OK;
00863 }
00864
00865 static int clean_rdman_dirties(redraw_man_t *rdman) {
00866 int r;
00867
00868 r = clean_rdman_coords(rdman);
00869 if(r != OK)
00870 return ERR;
00871
00872 r = clean_rdman_geos(rdman);
00873 if(r != OK)
00874 return ERR;
00875
00876 return OK;
00877 }
00878
00879
00880
00881
00882
00883
00884 #ifndef UNITTEST
00885 static void set_shape_stroke_param(shape_t *shape, cairo_t *cr) {
00886 cairo_set_line_width(cr, shape->stroke_width);
00887 }
00888
00889 static void fill_path_preserve(redraw_man_t *rdman) {
00890 cairo_fill_preserve(rdman->cr);
00891 }
00892
00893 static void fill_path(redraw_man_t *rdman) {
00894 cairo_fill(rdman->cr);
00895 }
00896
00897 static void stroke_path(redraw_man_t *rdman) {
00898 cairo_stroke(rdman->cr);
00899 }
00900 #else
00901 static void set_shape_stroke_param(shape_t *shape, cairo_t *cr) {
00902 }
00903
00904 static void fill_path_preserve(redraw_man_t *rdman) {
00905 }
00906
00907 static void fill_path(redraw_man_t *rdman) {
00908 }
00909
00910 static void stroke_path(redraw_man_t *rdman) {
00911 }
00912 #endif
00913
00914 static void draw_shape(redraw_man_t *rdman, cairo_t *cr, shape_t *shape) {
00915 paint_t *fill, *stroke;
00916
00917
00918
00919
00920 if(shape->fill || shape->stroke) {
00921 switch(shape->sh_type) {
00922 case SHT_PATH:
00923 sh_path_draw(shape, cr);
00924 break;
00925 case SHT_TEXT:
00926 sh_text_draw(shape, cr);
00927 break;
00928 case SHT_RECT:
00929 sh_rect_draw(shape, cr);
00930 break;
00931 #ifdef UNITTEST
00932 default:
00933 sh_dummy_fill(shape, cr);
00934 break;
00935 #endif
00936 }
00937
00938 fill = shape->fill;
00939 if(shape->fill) {
00940 fill->prepare(fill, cr);
00941 if(shape->stroke)
00942 fill_path_preserve(rdman);
00943 else
00944 fill_path(rdman);
00945 }
00946
00947 stroke = shape->stroke;
00948 if(stroke) {
00949 stroke->prepare(stroke, cr);
00950 set_shape_stroke_param(shape, cr);
00951 stroke_path(rdman);
00952 }
00953 }
00954 }
00955
00956 #ifndef UNITTEST
00957 static void clean_canvas(cairo_t *cr) {
00958
00959 cairo_set_source_rgb(cr, 1, 1, 1);
00960 cairo_paint(cr);
00961 }
00962
00963 static void clean_canvas_black(cairo_t *cr) {
00964
00965 cairo_set_source_rgba(cr, 0, 0, 0, 0);
00966 cairo_paint(cr);
00967 }
00968
00969 static void make_clip(cairo_t *cr, int n_dirty_areas,
00970 area_t **dirty_areas) {
00971 int i;
00972 area_t *area;
00973
00974 for(i = 0; i < n_dirty_areas; i++) {
00975 area = dirty_areas[i];
00976 cairo_rectangle(cr, area->x, area->y, area->w, area->h);
00977 }
00978 cairo_clip(cr);
00979 }
00980
00981 static void reset_clip(redraw_man_t *rdman) {
00982 cairo_reset_clip(rdman->backend);
00983 }
00984
00985 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
00986 area_t **dirty_areas) {
00987 if(n_dirty_areas)
00988 make_clip(rdman->backend, n_dirty_areas, dirty_areas);
00989
00990 cairo_paint(rdman->backend);
00991 }
00992 #else
00993 static void clean_canvas(cairo_t *cr) {
00994 }
00995
00996 static void clean_canvas_black(cairo_t *cr) {
00997 }
00998
00999 static void reset_clip(redraw_man_t *rdman) {
01000 }
01001
01002 static void copy_cr_2_backend(redraw_man_t *rdman, int n_dirty_areas,
01003 area_t **dirty_areas) {
01004 }
01005 #endif
01006
01007 static int is_geo_in_areas(geo_t *geo,
01008 int n_areas,
01009 area_t **areas) {
01010 int i;
01011
01012 for(i = 0; i < n_areas; i++) {
01013 if(is_overlay(geo->cur_area, areas[i]))
01014 return 1;
01015 }
01016 return 0;
01017 }
01018
01019 static void update_canvas_2_parent(redraw_man_t *rdman, coord_t *coord) {
01020 cairo_t *pcanvas, *canvas;
01021 cairo_surface_t *surface;
01022
01023 if(coord == rdman->root_coord)
01024 return;
01025
01026 canvas = coord->canvas;
01027 pcanvas = coord->parent->canvas;
01028 surface = cairo_get_target(canvas);
01029 cairo_set_source_surface(pcanvas, surface, 0, 0);
01030 cairo_paint_with_alpha(pcanvas, coord->opacity);
01031 }
01032
01033 static int draw_coord_shapes_in_areas(redraw_man_t *rdman,
01034 coord_t *coord,
01035 int n_areas,
01036 area_t **areas) {
01037 int dirty = 0;
01038 int r;
01039 geo_t *member;
01040 coord_t *child;
01041 cairo_t *canvas;
01042 int mem_idx;
01043
01044 if(coord->flags & COF_HIDDEN)
01045 return OK;
01046
01047 canvas = coord->canvas;
01048 member = FIRST_MEMBER(coord);
01049 mem_idx = 0;
01050 child = FIRST_CHILD(coord);
01051 while(child != NULL || member != NULL) {
01052 if(child && child->before_pmem == mem_idx) {
01053 r = draw_coord_shapes_in_areas(rdman, child, n_areas, areas);
01054 dirty |= r;
01055 child = NEXT_CHILD(child);
01056 } else {
01057 ASSERT(member != NULL);
01058 if((!(member->flags & GEF_HIDDEN)) &&
01059 is_geo_in_areas(member, n_areas, areas)) {
01060 draw_shape(rdman, canvas, member->shape);
01061 dirty = 1;
01062 }
01063
01064 member = NEXT_MEMBER(member);
01065 mem_idx++;
01066 }
01067 }
01068
01069 if(dirty && coord->flags & COF_OWN_CANVAS) {
01070 update_canvas_2_parent(rdman, coord);
01071 clean_canvas_black(coord->canvas);
01072 }
01073
01074 return dirty;
01075 }
01076
01077 static void draw_shapes_in_areas(redraw_man_t *rdman,
01078 int n_areas,
01079 area_t **areas) {
01080 draw_coord_shapes_in_areas(rdman, rdman->root_coord, n_areas, areas);
01081 }
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116 int rdman_redraw_changed(redraw_man_t *rdman) {
01117 int r;
01118 int n_dirty_areas;
01119 area_t **dirty_areas;
01120 event_t event;
01121 ob_factory_t *factory;
01122 subject_t *redraw;
01123
01124 r = clean_rdman_dirties(rdman);
01125 if(r != OK)
01126 return ERR;
01127
01128 n_dirty_areas = rdman->dirty_areas.num;
01129 dirty_areas = rdman->dirty_areas.ds;
01130 if(n_dirty_areas > 0) {
01131
01132
01133
01134 clean_canvas(rdman->cr);
01135 draw_shapes_in_areas(rdman, n_dirty_areas, dirty_areas);
01136 copy_cr_2_backend(rdman, rdman->dirty_areas.num,
01137 rdman->dirty_areas.ds);
01138 reset_clip(rdman);
01139 }
01140 rdman->dirty_areas.num = 0;
01141
01142
01143 free_free_objs(rdman);
01144
01145 factory = rdman_get_ob_factory(rdman);
01146 redraw = rdman_get_redraw_subject(rdman);
01147 event.type = EVT_RDMAN_REDRAW;
01148 event.tgt = event.cur_tgt = redraw;
01149 subject_notify(factory, redraw, &event);
01150
01151 return OK;
01152 }
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177
01178
01179
01180 int rdman_redraw_all(redraw_man_t *rdman) {
01181 area_t area;
01182 #ifndef UNITTEST
01183 cairo_surface_t *surface;
01184 #endif
01185 int r;
01186
01187 area.x = area.y = 0;
01188 #ifndef UNITTEST
01189 surface = cairo_get_target(rdman->cr);
01190 area.w = cairo_image_surface_get_width(surface);
01191 area.h = cairo_image_surface_get_height(surface);
01192 #else
01193 area.w = 1024;
01194 area.h = 1024;
01195 #endif
01196 add_dirty_area(rdman, &area);
01197
01198 r = rdman_redraw_changed(rdman);
01199 if(r != OK)
01200 return ERR;
01201
01202 return OK;
01203 }
01204
01205 int rdman_redraw_area(redraw_man_t *rdman, co_aix x, co_aix y,
01206 co_aix w, co_aix h) {
01207 area_t area;
01208 int r;
01209
01210 area.x = x;
01211 area.y = y;
01212 area.w = w;
01213 area.h = h;
01214 add_dirty_area(rdman, &area);
01215
01216 r = rdman_redraw_changed(rdman);
01217
01218 return r;
01219 }
01220
01221 geo_t *rdman_geos(redraw_man_t *rdman, geo_t *last) {
01222 geo_t *next;
01223 coord_t *coord;
01224
01225 if(last == NULL) {
01226 coord = rdman->root_coord;
01227 while(coord != NULL && FIRST_MEMBER(coord) == NULL)
01228 coord = preorder_coord_subtree(rdman->root_coord, coord);
01229 if(coord == NULL)
01230 return NULL;
01231 return FIRST_MEMBER(coord);
01232 }
01233
01234 coord = last->shape->coord;
01235 next = NEXT_MEMBER(last);
01236 while(next == NULL) {
01237 coord = preorder_coord_subtree(rdman->root_coord, coord);
01238 if(coord == NULL)
01239 return NULL;
01240 next = FIRST_MEMBER(coord);
01241 }
01242 return next;
01243 }
01244
01245 int rdman_force_clean(redraw_man_t *rdman) {
01246 int r;
01247
01248 r = clean_rdman_dirties(rdman);
01249
01250 return r;
01251 }
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310
01311
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341 static subject_t *ob_subject_alloc(ob_factory_t *factory) {
01342 redraw_man_t *rdman;
01343 subject_t *subject;
01344
01345 rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
01346 subject = elmpool_elm_alloc(rdman->subject_pool);
01347
01348 return subject;
01349 }
01350
01351 static void ob_subject_free(ob_factory_t *factory, subject_t *subject) {
01352 redraw_man_t *rdman;
01353
01354 rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
01355 elmpool_elm_free(rdman->subject_pool, subject);
01356 }
01357
01358 static observer_t *ob_observer_alloc(ob_factory_t *factory) {
01359 redraw_man_t *rdman;
01360 observer_t *observer;
01361
01362 rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
01363 observer = elmpool_elm_alloc(rdman->observer_pool);
01364
01365 return observer;
01366 }
01367
01368 static void ob_observer_free(ob_factory_t *factory, observer_t *observer) {
01369 redraw_man_t *rdman;
01370
01371 rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
01372 elmpool_elm_free(rdman->observer_pool, observer);
01373 }
01374
01375 static subject_t *ob_get_parent_subject(ob_factory_t *factory,
01376 subject_t *cur_subject) {
01377 redraw_man_t *rdman;
01378 coord_t *coord, *parent_coord;
01379 geo_t *geo;
01380 subject_t *parent;
01381
01382 rdman = MEM2OBJ(factory, redraw_man_t, ob_factory);
01383 switch(cur_subject->obj_type) {
01384 case OBJT_GEO:
01385 geo = (geo_t *)cur_subject->obj;
01386 parent_coord = geo->shape->coord;
01387 parent = parent_coord->mouse_event;
01388 break;
01389 case OBJT_COORD:
01390 coord = (coord_t *)cur_subject->obj;
01391 parent_coord = coord->parent;
01392 if(parent_coord == NULL) {
01393 parent = NULL;
01394 break;
01395 }
01396 parent = parent_coord->mouse_event;
01397 break;
01398 default:
01399 parent = NULL;
01400 break;
01401 }
01402
01403 return parent;
01404 }
01405
01406 #ifdef UNITTEST
01407
01408
01409 #include <CUnit/Basic.h>
01410 #include "mb_paint.h"
01411
01412 struct _sh_dummy {
01413 shape_t shape;
01414 co_aix x, y;
01415 co_aix w, h;
01416 int trans_cnt;
01417 int draw_cnt;
01418 };
01419
01420 void sh_dummy_free(shape_t *sh) {
01421 free(sh);
01422 }
01423
01424 shape_t *sh_dummy_new(redraw_man_t *rdman,
01425 co_aix x, co_aix y, co_aix w, co_aix h) {
01426 sh_dummy_t *dummy;
01427
01428 dummy = (sh_dummy_t *)malloc(sizeof(sh_dummy_t));
01429 if(dummy == NULL)
01430 return NULL;
01431
01432 memset(dummy, 0, sizeof(sh_dummy_t));
01433
01434 dummy->x = x;
01435 dummy->y = y;
01436 dummy->w = w;
01437 dummy->h = h;
01438 dummy->trans_cnt = 0;
01439 dummy->draw_cnt = 0;
01440 dummy->shape.free = sh_dummy_free;
01441
01442 rdman_shape_man(rdman, (shape_t *)dummy);
01443
01444 return (shape_t *)dummy;
01445 }
01446
01447 void sh_dummy_transform(shape_t *shape) {
01448 sh_dummy_t *dummy = (sh_dummy_t *)shape;
01449 co_aix poses[2][2];
01450 co_aix x1, y1, x2, y2;
01451
01452 if(shape->geo && shape->coord) {
01453 x1 = dummy->x;
01454 y1 = dummy->y;
01455 x2 = x1 + dummy->w;
01456 y2 = y1 + dummy->h;
01457
01458 coord_trans_pos(shape->coord, &x1, &y1);
01459 coord_trans_pos(shape->coord, &x2, &y2);
01460 poses[0][0] = x1;
01461 poses[0][1] = y1;
01462 poses[1][0] = x2;
01463 poses[1][1] = y2;
01464
01465 if(shape->geo)
01466 geo_from_positions(shape->geo, 2, poses);
01467 }
01468 dummy->trans_cnt++;
01469 }
01470
01471 void sh_dummy_fill(shape_t *shape, cairo_t *cr) {
01472 sh_dummy_t *dummy;
01473
01474 dummy = (sh_dummy_t *)shape;
01475 dummy->draw_cnt++;
01476 }
01477
01478 static void dummy_paint_prepare(paint_t *paint, cairo_t *cr) {
01479 }
01480
01481 static void dummy_paint_free(redraw_man_t *rdman, paint_t *paint) {
01482 if(paint)
01483 free(paint);
01484 }
01485
01486 paint_t *dummy_paint_new(redraw_man_t *rdman) {
01487 paint_t *paint;
01488
01489 paint = (paint_t *)malloc(sizeof(paint_t));
01490 if(paint == NULL)
01491 return NULL;
01492
01493 paint_init(paint, dummy_paint_prepare, dummy_paint_free);
01494
01495 return paint;
01496 }
01497
01498 static void test_rdman_redraw_changed(void) {
01499 coord_t *coords[3];
01500 shape_t *shapes[3];
01501 sh_dummy_t **dummys;
01502 paint_t *paint;
01503 redraw_man_t *rdman;
01504 redraw_man_t _rdman;
01505 int i;
01506
01507 dummys = (sh_dummy_t **)shapes;
01508
01509 rdman = &_rdman;
01510 redraw_man_init(rdman, NULL, NULL);
01511 paint = dummy_paint_new(rdman);
01512 for(i = 0; i < 3; i++) {
01513 shapes[i] = sh_dummy_new(rdman, 0, 0, 50, 50);
01514 rdman_paint_fill(rdman, paint, shapes[i]);
01515 coords[i] = rdman_coord_new(rdman, rdman->root_coord);
01516 coords[i]->matrix[2] = 10 + i * 100;
01517 coords[i]->matrix[5] = 10 + i * 100;
01518 rdman_coord_changed(rdman, coords[i]);
01519 rdman_add_shape(rdman, shapes[i], coords[i]);
01520 }
01521 rdman_redraw_all(rdman);
01522 CU_ASSERT(dummys[0]->trans_cnt == 1);
01523 CU_ASSERT(dummys[1]->trans_cnt == 1);
01524 CU_ASSERT(dummys[2]->trans_cnt == 1);
01525 CU_ASSERT(dummys[0]->draw_cnt == 1);
01526 CU_ASSERT(dummys[1]->draw_cnt == 1);
01527 CU_ASSERT(dummys[2]->draw_cnt == 1);
01528
01529 coords[2]->matrix[2] = 100;
01530 coords[2]->matrix[5] = 100;
01531 rdman_coord_changed(rdman, coords[0]);
01532 rdman_coord_changed(rdman, coords[2]);
01533 rdman_redraw_changed(rdman);
01534
01535 CU_ASSERT(dummys[0]->draw_cnt == 2);
01536 CU_ASSERT(dummys[1]->draw_cnt == 2);
01537 CU_ASSERT(dummys[2]->draw_cnt == 2);
01538
01539 rdman_paint_free(rdman, paint);
01540 redraw_man_destroy(rdman);
01541 }
01542
01543 static int test_free_pass = 0;
01544
01545 static void test_free(redraw_man_t *rdman, void *obj) {
01546 test_free_pass++;
01547 }
01548
01549 static void test_rdman_free_objs(void) {
01550 redraw_man_t *rdman;
01551 redraw_man_t _rdman;
01552 int i;
01553
01554 redraw_man_init(&_rdman, NULL, NULL);
01555 rdman = &_rdman;
01556
01557 test_free_pass = 0;
01558
01559 for(i = 0; i < 4; i++)
01560 add_free_obj(rdman, NULL, test_free);
01561
01562 redraw_man_destroy(rdman);
01563 CU_ASSERT(test_free_pass == 4);
01564 }
01565
01566 CU_pSuite get_redraw_man_suite(void) {
01567 CU_pSuite suite;
01568
01569 suite = CU_add_suite("Suite_redraw_man", NULL, NULL);
01570 CU_ADD_TEST(suite, test_rdman_redraw_changed);
01571 CU_ADD_TEST(suite, test_rdman_free_objs);
01572
01573 return suite;
01574 }
01575
01576 #endif