/* EINA - EFL data type library * Copyright (C) 2007-2008 Gustavo Sverzut Barbieri, Jorge Luis Zapata Muga * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; * if not, see . */ /* TODO * it is possible to have more than one tiler algorithm, but for now the * version Gustavo did is hardcoded here * http://blog.gustavobarbieri.com.br/2007/06/03/evas-now-using-rectangle-split-and-merge/ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "eina_config.h" #include "eina_private.h" #include "eina_tiler.h" #include "eina_cpu.h" /*============================================================================* * Local * *============================================================================*/ /* The splitter data types */ typedef struct list_node list_node_t; typedef struct list list_t; typedef struct rect rect_t; typedef struct rect_node rect_node_t; struct list_node { struct list_node *next; }; struct list { struct list_node *head; struct list_node *tail; }; struct rect { int right; int bottom; int left; int top; int width; int height; int area; }; struct rect_node { struct list_node _lst; struct rect rect; }; typedef struct splitter { Eina_Bool need_merge; list_t rects; } splitter_t; typedef struct list_node_pool { list_node_t *node; int len; int max; } list_node_pool_t; static const list_node_t list_node_zeroed = { NULL }; static const list_t list_zeroed = { NULL, NULL }; static list_node_pool_t list_node_pool = { NULL, 0, 1024 }; typedef struct _Eina_Iterator_Tiler { Eina_Iterator iterator; const Eina_Tiler *tiler; list_node_t *curr; Eina_Rectangle r; EINA_MAGIC } Eina_Iterator_Tiler; struct _Eina_Tiler { struct { int w, h; } tile; struct { Eina_Rectangle add, del; } last; Eina_Rectangle area; EINA_MAGIC splitter_t splitter; Eina_Bool rounding : 1; Eina_Bool strict : 1; }; #define EINA_MAGIC_CHECK_TILER(d, ...) \ do { \ if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_TILER)) \ { \ EINA_MAGIC_FAIL(d, EINA_MAGIC_TILER); \ return __VA_ARGS__; \ } \ } while(0) #define EINA_MAGIC_CHECK_TILER_ITERATOR(d, ...) \ do { \ if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_TILER_ITERATOR)) \ { \ EINA_MAGIC_FAIL(d, EINA_MAGIC_TILER_ITERATOR); \ return __VA_ARGS__; \ } \ } while(0) /* The Splitter algorithm */ static inline void rect_init(rect_t *r, int x, int y, int w, int h) { r->area = w * h; r->left = x; r->top = y; r->right = x + w; r->bottom = y + h; r->width = w; r->height = h; } static inline list_node_t * rect_list_node_pool_get(void) { if (list_node_pool.node) { list_node_t *node; node = list_node_pool.node; list_node_pool.node = node->next; list_node_pool.len--; return node; } else return malloc(sizeof(rect_node_t)); } static inline void rect_list_concat(list_t *rects, list_t *other) { if (!other->head) return; if (rects->tail) { rects->tail->next = other->head; rects->tail = other->tail; } else { rects->head = other->head; rects->tail = other->tail; } *other = list_zeroed; } static inline void rect_list_append_node(list_t *rects, list_node_t *node) { if (rects->tail) { rects->tail->next = node; rects->tail = node; } else { rects->head = node; rects->tail = node; } } static inline void rect_list_append(list_t *rects, const rect_t r) { rect_node_t *rect_node; rect_node = (rect_node_t *)rect_list_node_pool_get(); rect_node->rect = r; rect_node->_lst = list_node_zeroed; rect_list_append_node(rects, (list_node_t *)rect_node); } static inline void rect_list_append_xywh(list_t *rects, int x, int y, int w, int h) { rect_t r; rect_init(&r, x, y, w, h); rect_list_append(rects, r); } static inline void _calc_intra_rect_area(const rect_t a, const rect_t b, int *width, int *height) { int max_left, min_right, max_top, min_bottom; if (a.left < b.left) max_left = b.left; else max_left = a.left; if (a.right < b.right) min_right = a.right; else min_right = b.right; *width = min_right - max_left; if (a.top < b.top) max_top = b.top; else max_top = a.top; if (a.bottom < b.bottom) min_bottom = a.bottom; else min_bottom = b.bottom; *height = min_bottom - max_top; } static inline void _split_strict(list_t *dirty, const rect_t current, rect_t r) { int h_1, h_2, w_1, w_2; h_1 = current.top - r.top; h_2 = r.bottom - current.bottom; w_1 = current.left - r.left; w_2 = r.right - current.right; if (h_1 > 0) { /* .--.r (b) .---.r2 * | | | | * .-------.cur (a) .---.r '---' * | | | | -> | | + * | `--' | `---' * `-------' */ rect_list_append_xywh(dirty, r.left, r.top, r.width, h_1); r.height -= h_1; r.top = current.top; } if (h_2 > 0) { /* .-------.cur (a) * | .---. | .---.r * | | | | -> | | * `-------' `---' + .---.r2 * | | | | * `---'r (b) `---' */ rect_list_append_xywh(dirty, r.left, current.bottom, r.width, h_2); r.height -= h_2; } if (w_1 > 0) /* (b) r .----.cur (a) * .--|-. | .--.r2 .-.r * | | | | -> | | + | | * `--|-' | `--' `-' * `----' */ rect_list_append_xywh(dirty, r.left, r.top, w_1, r.height); /* not necessary to keep these, r (b) will be destroyed */ /* r.width -= w_1; */ /* r.left = current.left; */ if (w_2 > 0) /* .----.cur (a) * | | * | .-|--.r (b) .-.r .--.r2 * | | | | -> | | + | | * | `-|--' `-' `--' * `----' */ rect_list_append_xywh(dirty, current.right, r.top, w_2, r.height); /* not necessary to keep this, r (b) will be destroyed */ /* r.width -= w_2; */ } static inline void _calc_intra_outer_rect_area(const rect_t a, const rect_t b, rect_t *intra, rect_t *outer) { int min_left, max_left, min_right, max_right; int min_top, max_top, min_bottom, max_bottom; if (a.left < b.left) { max_left = b.left; min_left = a.left; } else { max_left = a.left; min_left = b.left; } if (a.right < b.right) { min_right = a.right; max_right = b.right; } else { min_right = b.right; max_right = a.right; } intra->left = max_left; intra->right = min_right; intra->width = min_right - max_left; outer->left = min_left; outer->right = max_right; outer->width = max_right - min_left; if (a.top < b.top) { max_top = b.top; min_top = a.top; } else { max_top = a.top; min_top = b.top; } if (a.bottom < b.bottom) { min_bottom = a.bottom; max_bottom = b.bottom; } else { min_bottom = b.bottom; max_bottom = a.bottom; } intra->top = max_top; intra->bottom = min_bottom; intra->height = min_bottom - max_top; if ((intra->width > 0) && (intra->height > 0)) intra->area = intra->width * intra->height; else intra->area = 0; outer->top = min_top; outer->bottom = max_bottom; outer->height = max_bottom - min_top; outer->area = outer->width * outer->height; } enum { SPLIT_FUZZY_ACTION_NONE, SPLIT_FUZZY_ACTION_SPLIT, SPLIT_FUZZY_ACTION_MERGE }; static inline int _split_fuzzy(list_t *dirty, const rect_t a, rect_t *b) { int h_1, h_2, w_1, w_2, action; h_1 = a.top - b->top; h_2 = b->bottom - a.bottom; w_1 = a.left - b->left; w_2 = b->right - a.right; action = SPLIT_FUZZY_ACTION_NONE; if (h_1 > 0) { /* .--.r (b) .---.r2 * | | | | * .-------.cur (a) .---.r '---' * | | | | -> | | + * | `--' | `---' * `-------' */ rect_list_append_xywh(dirty, b->left, b->top, b->width, h_1); b->height -= h_1; b->top = a.top; action = SPLIT_FUZZY_ACTION_SPLIT; } if (h_2 > 0) { /* .-------.cur (a) * | .---. | .---.r * | | | | -> | | * `-------' `---' + .---.r2 * | | | | * `---'r (b) `---' */ rect_list_append_xywh(dirty, b->left, a.bottom, b->width, h_2); b->height -= h_2; action = SPLIT_FUZZY_ACTION_SPLIT; } if (((w_1 > 0) || (w_2 > 0)) && (a.height == b->height)) return SPLIT_FUZZY_ACTION_MERGE; if (w_1 > 0) { /* (b) r .----.cur (a) * .--|-. | .--.r2 .-.r * | | | | -> | | + | | * `--|-' | `--' `-' * `----' */ rect_list_append_xywh(dirty, b->left, b->top, w_1, b->height); /* not necessary to keep these, r (b) will be destroyed */ /* b->width -= w_1; */ /* b->left = a.left; */ action = SPLIT_FUZZY_ACTION_SPLIT; } if (w_2 > 0) { /* .----.cur (a) * | | * | .-|--.r (b) .-.r .--.r2 * | | | | -> | | + | | * | `-|--' `-' `--' * `----' */ rect_list_append_xywh(dirty, a.right, b->top, w_2, b->height); /* not necessary to keep these, r (b) will be destroyed */ /* b->width -= w_2; */ action = SPLIT_FUZZY_ACTION_SPLIT; } return action; } #if 0 static void rect_list_node_pool_set_max(int max) { int diff; diff = list_node_pool.len - max; for (; diff > 0 && list_node_pool.node != NULL; diff--) { list_node_t *node; node = list_node_pool.node; list_node_pool.node = node->next; list_node_pool.len--; free(node); } list_node_pool.max = max; } #endif static void rect_list_node_pool_flush(void) { while (list_node_pool.node) { list_node_t *node; node = list_node_pool.node; list_node_pool.node = node->next; list_node_pool.len--; free(node); } } static inline void rect_list_node_pool_put(list_node_t *node) { if (list_node_pool.len < list_node_pool.max) { node->next = list_node_pool.node; list_node_pool.node = node; list_node_pool.len++; } else free(node); } #if 0 static void rect_print(const rect_t r) { printf("", r.left, r.top, r.width, r.height); } static void rect_list_print(const list_t rects) { list_node_t *node; int len; len = 0; for (node = rects.head; node != NULL; node = node->next) len++; printf("["); for (node = rects.head; node != NULL; node = node->next) { rect_print(((rect_node_t *)node)->rect); if (node->next) { putchar(','); if (len < 4) putchar(' '); else { putchar('\n'); putchar(' '); } } } printf("]\n"); } #endif static inline list_node_t * rect_list_unlink_next(list_t *rects, list_node_t *parent_node) { list_node_t *node; if (parent_node) { node = parent_node->next; parent_node->next = node->next; } else { node = rects->head; rects->head = node->next; } if (rects->tail == node) rects->tail = parent_node; *node = list_node_zeroed; return node; } static inline void rect_list_del_next(list_t *rects, list_node_t *parent_node) { list_node_t *node; node = rect_list_unlink_next(rects, parent_node); rect_list_node_pool_put(node); } static void rect_list_clear(list_t *rects) { list_node_t *node; node = rects->head; while (node) { list_node_t *aux; aux = node->next; rect_list_node_pool_put(node); node = aux; } *rects = list_zeroed; } static void rect_list_del_split_strict(list_t *rects, const rect_t del_r) { list_t modified = list_zeroed; list_node_t *cur_node, *prev_node; prev_node = NULL; cur_node = rects->head; while (cur_node) { int intra_width, intra_height; rect_t current; current = ((rect_node_t *)cur_node)->rect; _calc_intra_rect_area(del_r, current, &intra_width, &intra_height); if ((intra_width <= 0) || (intra_height <= 0)) { /* .---.current .---.del_r * | | | | * `---+---.del_r `---+---.current * | | | | * `---' `---' * no intersection, nothing to do */ prev_node = cur_node; cur_node = cur_node->next; } else if ((intra_width == current.width) && (intra_height == current.height)) { /* .-------.del_r * | .---. | * | | | | * | `---'current * `-------' * current is contained, remove from rects */ cur_node = cur_node->next; rect_list_del_next(rects, prev_node); } else { _split_strict(&modified, del_r, current); cur_node = cur_node->next; rect_list_del_next(rects, prev_node); } } rect_list_concat(rects, &modified); } #if 0 static void rect_list_add_split_strict(list_t *rects, list_node_t *node) { list_t dirty = list_zeroed; list_t new_dirty = list_zeroed; list_node_t *cur_node; if (!rects->head) { rect_list_append_node(rects, node); return; } rect_list_append_node(&dirty, node); cur_node = rects->head; while (dirty.head) { rect_t current; if (!cur_node) { rect_list_concat(rects, &dirty); break; } current = ((rect_node_t *)cur_node)->rect; while (dirty.head) { int intra_width, intra_height; rect_t r; r = ((rect_node_t *)dirty.head)->rect; _calc_intra_rect_area(r, current, &intra_width, &intra_height); if ((intra_width == r.width) && (intra_height == r.height)) /* .-------.cur * | .---.r| * | | | | * | `---' | * `-------' */ rect_list_del_next(&dirty, NULL); else if ((intra_width <= 0) || (intra_height <= 0)) { /* .---.cur .---.r * | | | | * `---+---.r `---+---.cur * | | | | * `---' `---' */ list_node_t *tmp; tmp = rect_list_unlink_next(&dirty, NULL); rect_list_append_node(&new_dirty, tmp); } else { _split_strict(&new_dirty, current, r); rect_list_del_next(&dirty, NULL); } } dirty = new_dirty; new_dirty = list_zeroed; cur_node = cur_node->next; } } #endif static list_node_t * rect_list_add_split_fuzzy(list_t *rects, list_node_t *node, int accepted_error) { list_t dirty = list_zeroed; list_node_t *old_last; old_last = rects->tail; if (!rects->head) { rect_list_append_node(rects, node); return old_last; } rect_list_append_node(&dirty, node); while (dirty.head) { list_node_t *d_node, *cur_node, *prev_cur_node; int keep_dirty; rect_t r; d_node = rect_list_unlink_next(&dirty, NULL); r = ((rect_node_t *)d_node)->rect; prev_cur_node = NULL; cur_node = rects->head; keep_dirty = 1; while (cur_node) { int area, action; rect_t current, intra, outer; current = ((rect_node_t *)cur_node)->rect; _calc_intra_outer_rect_area(r, current, &intra, &outer); area = current.area + r.area - intra.area; if ((intra.width == r.width) && (intra.height == r.height)) { /* .-------.cur * | .---.r| * | | | | * | `---' | * `-------' */ keep_dirty = 0; break; } else if ((intra.width == current.width) && (intra.height == current.height)) { /* .-------.r * | .---.cur * | | | | * | `---' | * `-------' */ if (old_last == cur_node) old_last = prev_cur_node; cur_node = cur_node->next; rect_list_del_next(rects, prev_cur_node); } else if ((outer.area - area) <= accepted_error) { /* .-----------. bounding box (outer) * |.---. .---.| * ||cur| |r || * || | | || * |`---' `---'| * `-----------' * merge them, remove both and add merged */ rect_node_t *n; if (old_last == cur_node) old_last = prev_cur_node; n = (rect_node_t *)rect_list_unlink_next( rects, prev_cur_node); n->rect = outer; rect_list_append_node(&dirty, (list_node_t *)n); keep_dirty = 0; break; } else if (intra.area <= accepted_error) { /* .---.cur .---.r * | | | | * `---+---.r `---+---.cur * | | | | * `---' `---' * no split, no merge */ prev_cur_node = cur_node; cur_node = cur_node->next; } else { /* split is required */ action = _split_fuzzy(&dirty, current, &r); if (action == SPLIT_FUZZY_ACTION_MERGE) { /* horizontal merge is possible: remove both, add merged */ rect_node_t *n; if (old_last == cur_node) old_last = prev_cur_node; n = (rect_node_t *)rect_list_unlink_next( rects, prev_cur_node); n->rect.left = outer.left; n->rect.width = outer.width; n->rect.right = outer.right; n->rect.area = outer.width * r.height; rect_list_append_node(&dirty, (list_node_t *)n); } else if (action == SPLIT_FUZZY_ACTION_NONE) { /* * this rect check was totally useless, * should never happen */ /* prev_cur_node = cur_node; */ /* cur_node = cur_node->next; */ printf("Should not get here!\n"); abort(); } keep_dirty = 0; break; } } if (EINA_UNLIKELY(keep_dirty)) rect_list_append_node(rects, d_node); else rect_list_node_pool_put(d_node); } return old_last; } static inline void _calc_outer_rect_area(const rect_t a, const rect_t b, rect_t *outer) { int min_left, max_right; int min_top, max_bottom; if (a.left < b.left) min_left = a.left; else min_left = b.left; if (a.right < b.right) max_right = b.right; else max_right = a.right; outer->left = min_left; outer->right = max_right; outer->width = max_right - min_left; if (a.top < b.top) min_top = a.top; else min_top = b.top; if (a.bottom < b.bottom) max_bottom = b.bottom; else max_bottom = a.bottom; outer->top = min_top; outer->bottom = max_bottom; outer->height = max_bottom - min_top; outer->area = outer->width * outer->height; } static void rect_list_merge_rects(list_t *rects, list_t *to_merge, int accepted_error) { while (to_merge->head) { list_node_t *node, *parent_node; rect_t r1; int merged; r1 = ((rect_node_t *)to_merge->head)->rect; merged = 0; parent_node = NULL; node = rects->head; while (node) { rect_t r2, outer; int area; r2 = ((rect_node_t *)node)->rect; _calc_outer_rect_area(r1, r2, &outer); area = r1.area + r2.area; /* intra area is taken as 0 */ if (outer.area - area <= accepted_error) { /* * remove both r1 and r2, create r3 * actually r3 uses r2 instance, saves memory */ rect_node_t *n; n = (rect_node_t *)rect_list_unlink_next( rects, parent_node); n->rect = outer; rect_list_append_node(to_merge, (list_node_t *)n); merged = 1; break; } parent_node = node; node = node->next; } if (!merged) { list_node_t *n; n = rect_list_unlink_next(to_merge, NULL); rect_list_append_node(rects, n); } else rect_list_del_next(to_merge, NULL); } } static void rect_list_add_split_fuzzy_and_merge(list_t *rects, list_node_t *node, int split_accepted_error, int merge_accepted_error) { list_node_t *n; n = rect_list_add_split_fuzzy(rects, node, split_accepted_error); if (n && n->next) { list_t to_merge; /* split list into 2 segments, already merged and to merge */ to_merge.head = n->next; to_merge.tail = rects->tail; rects->tail = n; n->next = NULL; rect_list_merge_rects(rects, &to_merge, merge_accepted_error); } } static inline void _splitter_new(Eina_Tiler *t) { t->splitter.rects = list_zeroed; t->splitter.need_merge = EINA_FALSE; } static inline void _splitter_del(Eina_Tiler *t) { rect_list_clear(&t->splitter.rects); rect_list_node_pool_flush(); } static inline void _splitter_tile_size_set(Eina_Tiler *t, int w EINA_UNUSED, int h EINA_UNUSED) { /* TODO are w and h used for something? */ t->splitter.rects = list_zeroed; } static inline Eina_Bool _splitter_rect_add(Eina_Tiler *t, Eina_Rectangle *rect) { rect_node_t *rn; //printf("ACCOUNTING[1]: add_redraw: %4d,%4d %3dx%3d\n", x, y, w, h); if (t->rounding) { // FIXME: Seems that this trigger overdraw bug in Evas implementation and // was disable. Need to investigate. rect->x >>= 1; rect->y >>= 1; rect->w += 2; rect->w >>= 1; rect->h += 2; rect->h >>= 1; } rn = (rect_node_t *)rect_list_node_pool_get(); rn->_lst = list_node_zeroed; rect_init(&rn->rect, rect->x, rect->y, rect->w, rect->h); //printf("ACCOUNTING[2]: add_redraw: %4d,%4d %3dx%3d\n", x, y, w, h); //testing on my core2 duo desktop - fuzz of 32 or 48 is best. rect_list_add_split_fuzzy_and_merge(&t->splitter.rects, (list_node_t *)rn, t->tile.w * t->tile.h, t->tile.w * t->tile.h); return EINA_TRUE; } static inline void _splitter_rect_del(Eina_Tiler *t, Eina_Rectangle *rect) { rect_t r; if (!t->splitter.rects.head) return; if (t->rounding) { rect->x += 1; rect->y += 1; rect->x >>= 1; rect->y >>= 1; rect->w -= 1; rect->w >>= 1; rect->h -= 1; rect->h >>= 1; } if ((rect->w <= 0) || (rect->h <= 0)) return; rect_init(&r, rect->x, rect->y, rect->w, rect->h); //fprintf(stderr, "ACCOUNTING: del_redraw: %4d,%4d %3dx%3d\n", x, y, w, h); rect_list_del_split_strict(&t->splitter.rects, r); t->splitter.need_merge = EINA_TRUE; return; } static inline void _splitter_clear(Eina_Tiler *t) { rect_list_clear(&t->splitter.rects); t->splitter.need_merge = EINA_FALSE; } /* end of splitter algorithm */ static Eina_Bool _iterator_next(Eina_Iterator_Tiler *it, void **data) { list_node_t *n; for (n = it->curr; n; n = n->next) { rect_t cur; cur = ((rect_node_t *)n)->rect; if (it->tiler->rounding) { it->r.x = cur.left << 1; it->r.y = cur.top << 1; it->r.w = cur.width << 1; it->r.h = cur.height << 1; } else { it->r.x = cur.left; it->r.y = cur.top; it->r.w = cur.width; it->r.h = cur.height; } if (eina_rectangle_intersection(&it->r, &it->tiler->area) == EINA_FALSE) continue; if ((it->r.w <= 0) || (it->r.h <= 0)) continue; it->curr = n->next; *(Eina_Rectangle **)data = &it->r; return EINA_TRUE; } return EINA_FALSE; } static void *_iterator_get_container(Eina_Iterator_Tiler *it) { EINA_MAGIC_CHECK_TILER_ITERATOR(it, NULL); return (void *)it->tiler; } static void _iterator_free(Eina_Iterator_Tiler *it) { EINA_MAGIC_CHECK_TILER_ITERATOR(it); free(it); } /*============================================================================* * Global * *============================================================================*/ /*============================================================================* * API * *============================================================================*/ EAPI Eina_Tiler *eina_tiler_new(int w, int h) { Eina_Tiler *t; EINA_SAFETY_ON_TRUE_RETURN_VAL((w <= 0) || (h <= 0), NULL); t = calloc(1, sizeof(Eina_Tiler)); t->last.add.w = -1; t->last.add.h = -1; t->last.del.w = -1; t->last.del.h = -1; t->area.w = w; t->area.h = h; t->tile.w = 32; t->tile.h = 32; t->rounding = EINA_TRUE; EINA_MAGIC_SET(t, EINA_MAGIC_TILER); _splitter_new(t); return t; } EAPI void eina_tiler_free(Eina_Tiler *t) { if (!t) return; EINA_MAGIC_CHECK_TILER(t); _splitter_del(t); free(t); } EAPI void eina_tiler_area_size_set(Eina_Tiler *t, int w, int h) { EINA_MAGIC_CHECK_TILER(t); if ((w <= 0) || (h <= 0)) return; t->area.w = w; t->area.h = h; } EAPI void eina_tiler_area_size_get(const Eina_Tiler *t, int *w, int *h) { EINA_MAGIC_CHECK_TILER(t); if (w) *w = t->area.w; if (h) *h = t->area.h; } EAPI void eina_tiler_tile_size_set(Eina_Tiler *t, int w, int h) { EINA_MAGIC_CHECK_TILER(t); if ((w <= 0) || (h <= 0)) return; if ((t->tile.w == w) && (t->tile.h == h)) return; if (w == 1 || h == 1) t->rounding = EINA_FALSE; t->tile.w = w; t->tile.h = h; _splitter_tile_size_set(t, w, h); } EAPI Eina_Bool eina_tiler_empty(const Eina_Tiler *t) { EINA_MAGIC_CHECK_TILER(t, EINA_TRUE); return ((!t->splitter.rects.head) && (!t->splitter.rects.tail)); } typedef struct _Rectangle_Same { unsigned long long x, y; } Rectangle_Same; static inline Eina_Bool _rect_same(Eina_Rectangle *rec1, Eina_Rectangle *rec2) { // this is ok because all the rects being compared will be aligned to 8bytes Rectangle_Same *same1 = (Rectangle_Same *)rec1; Rectangle_Same *same2 = (Rectangle_Same *)rec2; return ((same1->x == same2->x) && (same1->y == same2->y)); } EAPI Eina_Bool eina_tiler_rect_add(Eina_Tiler *t, const Eina_Rectangle *r) { Eina_Rectangle tmp __attribute__ ((aligned (8))); EINA_MAGIC_CHECK_TILER(t, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(r, EINA_FALSE); if ((r->w <= 0) || (r->h <= 0)) return EINA_FALSE; tmp = *r; if (eina_rectangle_intersection(&tmp, &t->area) == EINA_FALSE) return EINA_FALSE; if ((tmp.w <= 0) || (tmp.h <= 0)) return EINA_FALSE; if (_rect_same(&tmp, &t->last.add)) return EINA_TRUE; t->last.add = tmp; t->last.del.w = t->last.del.h = -1; return _splitter_rect_add(t, &tmp); } EAPI void eina_tiler_rect_del(Eina_Tiler *t, const Eina_Rectangle *r) { Eina_Rectangle tmp __attribute__ ((aligned (8))); EINA_MAGIC_CHECK_TILER(t); EINA_SAFETY_ON_NULL_RETURN(r); if ((r->w <= 0) || (r->h <= 0)) return; tmp = *r; if (eina_rectangle_intersection(&tmp, &t->area) == EINA_FALSE) return; if ((tmp.w <= 0) || (tmp.h <= 0)) return; if (_rect_same(&tmp, &t->last.del)) return; t->last.del = tmp; t->last.add.w = t->last.add.h = -1; _splitter_rect_del(t, &tmp); } EAPI void eina_tiler_clear(Eina_Tiler *t) { EINA_MAGIC_CHECK_TILER(t); _splitter_clear(t); t->last.add.w = -1; t->last.add.h = -1; t->last.del.w = -1; t->last.del.h = -1; } EAPI void eina_tiler_strict_set(Eina_Tiler *t, Eina_Bool strict) { EINA_MAGIC_CHECK_TILER(t); t->strict = strict; } EAPI Eina_Iterator *eina_tiler_iterator_new(const Eina_Tiler *t) { Eina_Iterator_Tiler *it; if (!t) return NULL; EINA_MAGIC_CHECK_TILER(t, NULL); it = calloc(1, sizeof (Eina_Iterator_Tiler)); if (!it) return NULL; it->tiler = t; if (t->splitter.need_merge == EINA_TRUE) { splitter_t *sp; list_t to_merge; to_merge = t->splitter.rects; if (t->strict) { rect_node_t *rn; list_node_t *n; // round up rects to tb->tile_size.w and tb->tile_size.h to_merge = list_zeroed; for (n = t->splitter.rects.head; n; n = n->next) { int x1, x2, y1, y2; x1 = ((rect_node_t *)n)->rect.left; x2 = x1 + ((rect_node_t *)n)->rect.width; y1 = ((rect_node_t *)n)->rect.top; y2 = y1 + ((rect_node_t *)n)->rect.height; x1 = t->tile.w * (x1 / t->tile.w); y1 = t->tile.h * (y1 / t->tile.h); x2 = t->tile.w * ((x2 + t->tile.w - 1) / t->tile.w); y2 = t->tile.h * ((y2 + t->tile.h - 1) / t->tile.h); rn = (rect_node_t *)rect_list_node_pool_get(); rn->_lst = list_node_zeroed; rect_init(&rn->rect, x1, y1, x2 - x1, y2 - y1); rect_list_add_split_fuzzy_and_merge(&to_merge, (list_node_t *)rn, t->tile.w * t->tile.h, t->tile.w * t->tile.h); } } sp = (splitter_t *)&(t->splitter); sp->rects = list_zeroed; rect_list_merge_rects(&sp->rects, &to_merge, t->tile.w * t->tile.h); sp->need_merge = 0; } it->curr = it->tiler->splitter.rects.head; if (!it->tiler->splitter.rects.head) { free(it); return NULL; } it->iterator.version = EINA_ITERATOR_VERSION; it->iterator.next = FUNC_ITERATOR_NEXT(_iterator_next); it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( _iterator_get_container); it->iterator.free = FUNC_ITERATOR_FREE(_iterator_free); EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); EINA_MAGIC_SET(it, EINA_MAGIC_TILER_ITERATOR); return &it->iterator; } EAPI Eina_Bool eina_tiler_union(Eina_Tiler *dst, Eina_Tiler *src) { Eina_Iterator *itr; Eina_Rectangle *rect, _rect; if ((!src) || (!dst)) return EINA_FALSE; EINA_MAGIC_CHECK_TILER(src, EINA_FALSE); EINA_MAGIC_CHECK_TILER(dst, EINA_FALSE); if ((src->area.w <= 0) || (src->area.h <=0)) return EINA_FALSE; dst->area.w = MAX(src->area.w, dst->area.w); dst->area.h = MAX(src->area.h, dst->area.h); itr = eina_tiler_iterator_new(src); if (!itr) return EINA_FALSE; EINA_ITERATOR_FOREACH(itr, rect) { _rect = *rect; if (src->rounding) { _rect.w -= 1; _rect.h -= 1; } _splitter_rect_add(dst, &_rect); } if (rect) dst->last.add = *rect; eina_iterator_free(itr); return EINA_TRUE; } EAPI Eina_Bool eina_tiler_subtract(Eina_Tiler *dst, Eina_Tiler *src) { Eina_Iterator *itr; Eina_Rectangle *rect, _rect; if ((!src) || (!dst)) return EINA_FALSE; EINA_MAGIC_CHECK_TILER(src, EINA_FALSE); EINA_MAGIC_CHECK_TILER(dst, EINA_FALSE); if ((src->area.w <= 0) || (src->area.h <= 0)) return EINA_FALSE; itr = eina_tiler_iterator_new(src); if (!itr) return EINA_FALSE; EINA_ITERATOR_FOREACH(itr, rect) { _rect = *rect; if (src->rounding) { _rect.w -= 1; _rect.h -= 1; } _splitter_rect_del(dst, &_rect); } if (rect) dst->last.del = *rect; eina_iterator_free(itr); return EINA_TRUE; } EAPI Eina_Tiler * eina_tiler_intersection(Eina_Tiler *t1, Eina_Tiler *t2) { Eina_Tiler *t = NULL; int w, h; Eina_Iterator *itr1 = NULL, *itr2 = NULL; Eina_Rectangle rect, *rect1 = NULL, *rect2 = NULL; EINA_MAGIC_CHECK_TILER(t1, NULL); EINA_MAGIC_CHECK_TILER(t2, NULL); if (!(eina_rectangles_intersect(&t1->area, &t2->area))) return NULL; itr1 = eina_tiler_iterator_new(t1); itr2 = eina_tiler_iterator_new(t2); if (!((itr1) && (itr2))) goto cleanup; if ((!eina_iterator_next(itr1, (void**)(void*)(&rect1))) && (!rect1)) goto cleanup; if ((!eina_iterator_next(itr2, (void**)(void*)(&rect2))) && (!rect2)) goto cleanup; w = MIN(t1->area.w, t2->area.w); h = MIN(t1->area.h, t2->area.h); t = eina_tiler_new(w, h); while((rect1) && (rect2)) { if (eina_rectangles_intersect(rect1, rect2)) { rect.x = MAX(rect1->x, rect2->x); rect.y = MAX(rect1->y, rect2->y); rect.w = MIN(rect1->x + rect1->w, rect2->x + rect2->w) - rect.x; rect.h = MIN(rect1->y + rect1->h, rect2->y + rect2->h) - rect.y; if ((t1->rounding) || (t2->rounding)) { rect.w -= 1; rect.h -= 1; } _splitter_rect_add(t, &rect); t->last.add = rect; } if (rect1->x + rect1->w < rect2->x + rect2->w) { if(eina_iterator_next(itr1, (void**)&rect1)) continue; } else if (rect1->x + rect1->w > rect2->x + rect2->w) { if (eina_iterator_next(itr2, (void**)&rect2)) continue; } if (eina_iterator_next(itr1, (void**)&rect1)) continue; if (eina_iterator_next(itr2, (void**)&rect2)) continue; break; } if (eina_tiler_empty(t)) { eina_tiler_free(t); t = NULL; } cleanup: eina_iterator_free(itr1); eina_iterator_free(itr2); return t; } EAPI Eina_Bool eina_tiler_equal(const Eina_Tiler *t1, const Eina_Tiler *t2) { Eina_Iterator *itr1 = NULL, *itr2 = NULL; Eina_Rectangle *rect1 = NULL, *rect2 = NULL; Eina_Bool next_t1 = EINA_FALSE, next_t2 = EINA_FALSE; Eina_Bool r = EINA_FALSE; EINA_MAGIC_CHECK_TILER(t1, EINA_FALSE); EINA_MAGIC_CHECK_TILER(t2, EINA_FALSE); if (!(eina_rectangles_intersect(&t1->area, &t2->area))) return EINA_FALSE; itr1 = eina_tiler_iterator_new(t1); itr2 = eina_tiler_iterator_new(t2); if (!((itr1) && (itr2))) goto cleanup; if ((!eina_iterator_next(itr1, (void**)(void*)(&rect1))) && (!rect1)) goto cleanup; if ((!eina_iterator_next(itr2, (void**)(void*)(&rect2))) && (!rect2)) goto cleanup; while((rect1) && (rect2)) { if (!eina_rectangles_intersect(rect1, rect2)) goto cleanup; if ((rect1->x != rect2->x) || (rect1->y != rect2->y) || (rect1->w != rect2->w) || (rect1->h != rect2->h)) goto cleanup; next_t1 = eina_iterator_next(itr1, (void**)&rect1); next_t2 = eina_iterator_next(itr2, (void**)&rect2); if (next_t1 != next_t2) goto cleanup; if (!next_t1 && !next_t2) break; } r = EINA_TRUE; cleanup: eina_iterator_free(itr1); eina_iterator_free(itr2); return r; } struct _Eina_Tile_Grid_Slicer_Iterator { Eina_Iterator iterator; Eina_Tile_Grid_Slicer priv; }; typedef struct _Eina_Tile_Grid_Slicer_Iterator Eina_Tile_Grid_Slicer_Iterator; static void eina_tile_grid_slicer_iterator_free(Eina_Tile_Grid_Slicer_Iterator *it) { EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_NONE); free(it); } static Eina_Bool eina_tile_grid_slicer_iterator_next(Eina_Tile_Grid_Slicer_Iterator *it, void **data) { return eina_tile_grid_slicer_next (&it->priv, (const Eina_Tile_Grid_Info **)data); } EAPI Eina_Iterator * eina_tile_grid_slicer_iterator_new(int x, int y, int w, int h, int tile_w, int tile_h) { Eina_Tile_Grid_Slicer_Iterator *it; if ((x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (tile_w <= 0) || (tile_h <= 0)) return NULL; it = calloc(1, sizeof(*it)); if (!it) return NULL; EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); it->iterator.version = EINA_ITERATOR_VERSION; it->iterator.next = FUNC_ITERATOR_NEXT(eina_tile_grid_slicer_iterator_next); it->iterator.free = FUNC_ITERATOR_FREE(eina_tile_grid_slicer_iterator_free); eina_tile_grid_slicer_setup(&it->priv, x, y, w, h, tile_w, tile_h); return &it->iterator; }