1 /* EINA - EFL data type library
2 * Copyright (C) 2007-2008 Gustavo Sverzut Barbieri, Jorge Luis Zapata Muga
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
20 * it is possible to have more than one tiler algorithm, but for now the
21 * version Gustavo did is hardcoded here
22 * http://blog.gustavobarbieri.com.br/2007/06/03/evas-now-using-rectangle-split-and-merge/
32 #include "eina_config.h"
33 #include "eina_private.h"
34 #include "eina_tiler.h"
35 #include "eina_error.h"
37 /*============================================================================*
39 *============================================================================*/
41 /* The splitter data types */
42 typedef struct list_node list_node_t;
43 typedef struct list list_t;
44 typedef struct rect rect_t;
45 typedef struct rect_node rect_node_t;
49 struct list_node *next;
54 struct list_node *head;
55 struct list_node *tail;
71 struct list_node _lst;
75 typedef struct splitter
81 typedef struct list_node_pool
89 static const list_node_t list_node_zeroed = { NULL };
90 static const list_t list_zeroed = { NULL, NULL };
91 static list_node_pool_t list_node_pool = { NULL, 0, 1024 };
94 typedef struct _Eina_Iterator_Tiler
96 Eina_Iterator iterator;
97 const Eina_Tiler *tiler;
101 } Eina_Iterator_Tiler;
114 #define EINA_MAGIC_CHECK_TILER(d, ...) \
116 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_TILER)) \
118 EINA_MAGIC_FAIL(d, EINA_MAGIC_TILER); \
119 return __VA_ARGS__; \
124 #define EINA_MAGIC_CHECK_TILER_ITERATOR(d, ...) \
126 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_TILER_ITERATOR)) \
128 EINA_MAGIC_FAIL(d, EINA_MAGIC_TILER_ITERATOR); \
129 return __VA_ARGS__; \
133 /* The Splitter algorithm */
134 static inline void rect_init(rect_t *r, int x, int y, int w, int h)
148 static inline list_node_t *
149 rect_list_node_pool_get(void)
151 if (list_node_pool.node)
155 node = list_node_pool.node;
156 list_node_pool.node = node->next;
157 list_node_pool.len--;
162 return malloc(sizeof(rect_node_t));
166 static inline void rect_list_concat(list_t *rects, list_t *other)
173 rects->tail->next = other->head;
174 rects->tail = other->tail;
178 rects->head = other->head;
179 rects->tail = other->tail;
182 *other = list_zeroed;
185 static inline void rect_list_append_node(list_t *rects, list_node_t *node)
189 rects->tail->next = node;
199 static inline void rect_list_append(list_t *rects, const rect_t r)
201 rect_node_t *rect_node;
203 rect_node = (rect_node_t *)rect_list_node_pool_get();
205 rect_node->_lst = list_node_zeroed;
207 rect_list_append_node(rects, (list_node_t *)rect_node);
210 static inline void rect_list_append_xywh(list_t *rects,
218 rect_init(&r, x, y, w, h);
219 rect_list_append(rects, r);
222 static inline void _calc_intra_rect_area(const rect_t a, const rect_t b,
223 int *width, int *height)
225 int max_left, min_right, max_top, min_bottom;
232 if (a.right < b.right)
237 *width = min_right - max_left;
244 if (a.bottom < b.bottom)
245 min_bottom = a.bottom;
247 min_bottom = b.bottom;
249 *height = min_bottom - max_top;
252 static inline void _split_strict(list_t *dirty, const rect_t current, rect_t r)
254 int h_1, h_2, w_1, w_2;
256 h_1 = current.top - r.top;
257 h_2 = r.bottom - current.bottom;
258 w_1 = current.left - r.left;
259 w_2 = r.right - current.right;
265 * .-------.cur (a) .---.r '---'
270 rect_list_append_xywh(dirty, r.left, r.top, r.width, h_1);
280 * `-------' `---' + .---.r2
284 rect_list_append_xywh(dirty, r.left, current.bottom, r.width,
290 /* (b) r .----.cur (a)
291 * .--|-. | .--.r2 .-.r
292 * | | | | -> | | + | |
296 rect_list_append_xywh(dirty, r.left, r.top, w_1, r.height); /* not necessary to keep these, r (b) will be destroyed */
298 /* r.width -= w_1; */
299 /* r.left = current.left; */
304 * | .-|--.r (b) .-.r .--.r2
305 * | | | | -> | | + | |
309 rect_list_append_xywh(dirty, current.right, r.top, w_2,
310 r.height); /* not necessary to keep this, r (b) will be destroyed */
312 /* r.width -= w_2; */
315 static inline void _calc_intra_outer_rect_area(const rect_t a, const rect_t b,
316 rect_t *intra, rect_t *outer)
318 int min_left, max_left, min_right, max_right;
319 int min_top, max_top, min_bottom, max_bottom;
332 if (a.right < b.right)
343 intra->left = max_left;
344 intra->right = min_right;
345 intra->width = min_right - max_left;
347 outer->left = min_left;
348 outer->right = max_right;
349 outer->width = max_right - min_left;
362 if (a.bottom < b.bottom)
364 min_bottom = a.bottom;
365 max_bottom = b.bottom;
369 min_bottom = b.bottom;
370 max_bottom = a.bottom;
373 intra->top = max_top;
374 intra->bottom = min_bottom;
375 intra->height = min_bottom - max_top;
376 if ((intra->width > 0) && (intra->height > 0))
377 intra->area = intra->width * intra->height;
381 outer->top = min_top;
382 outer->bottom = max_bottom;
383 outer->height = max_bottom - min_top;
384 outer->area = outer->width * outer->height;
389 SPLIT_FUZZY_ACTION_NONE,
390 SPLIT_FUZZY_ACTION_SPLIT,
391 SPLIT_FUZZY_ACTION_MERGE
394 static inline int _split_fuzzy(list_t *dirty, const rect_t a, rect_t *b)
396 int h_1, h_2, w_1, w_2, action;
398 h_1 = a.top - b->top;
399 h_2 = b->bottom - a.bottom;
400 w_1 = a.left - b->left;
401 w_2 = b->right - a.right;
403 action = SPLIT_FUZZY_ACTION_NONE;
409 * .-------.cur (a) .---.r '---'
414 rect_list_append_xywh(dirty, b->left, b->top, b->width, h_1);
417 action = SPLIT_FUZZY_ACTION_SPLIT;
425 * `-------' `---' + .---.r2
429 rect_list_append_xywh(dirty, b->left, a.bottom, b->width, h_2);
431 action = SPLIT_FUZZY_ACTION_SPLIT;
434 if (((w_1 > 0) || (w_2 > 0)) && (a.height == b->height))
435 return SPLIT_FUZZY_ACTION_MERGE;
439 /* (b) r .----.cur (a)
440 * .--|-. | .--.r2 .-.r
441 * | | | | -> | | + | |
445 rect_list_append_xywh(dirty, b->left, b->top, w_1, b->height);
446 /* not necessary to keep these, r (b) will be destroyed */
447 /* b->width -= w_1; */
448 /* b->left = a.left; */
449 action = SPLIT_FUZZY_ACTION_SPLIT;
456 * | .-|--.r (b) .-.r .--.r2
457 * | | | | -> | | + | |
461 rect_list_append_xywh(dirty, a.right, b->top, w_2, b->height);
462 /* not necessary to keep these, r (b) will be destroyed */
463 /* b->width -= w_2; */
464 action = SPLIT_FUZZY_ACTION_SPLIT;
471 static void rect_list_node_pool_set_max(int max)
475 diff = list_node_pool.len - max;
476 for (; diff > 0 && list_node_pool.node != NULL; diff--)
480 node = list_node_pool.node;
481 list_node_pool.node = node->next;
482 list_node_pool.len--;
487 list_node_pool.max = max;
491 static void rect_list_node_pool_flush(void)
493 while (list_node_pool.node)
497 node = list_node_pool.node;
498 list_node_pool.node = node->next;
499 list_node_pool.len--;
507 static inline void rect_list_node_pool_put(list_node_t *node)
509 if (list_node_pool.len < list_node_pool.max)
511 node->next = list_node_pool.node;
512 list_node_pool.node = node;
513 list_node_pool.len++;
520 static void rect_print(const rect_t r)
522 printf("<rect(%d, %d, %d, %d)>", r.left, r.top, r.width, r.height);
525 static void rect_list_print(const list_t rects)
531 for (node = rects.head; node != NULL; node = node->next)
535 for (node = rects.head; node != NULL; node = node->next)
537 rect_print(((rect_node_t *)node)->rect);
554 static inline list_node_t *
555 rect_list_unlink_next(list_t *rects, list_node_t *parent_node)
561 node = parent_node->next;
562 parent_node->next = node->next;
567 rects->head = node->next;
570 if (rects->tail == node)
571 rects->tail = parent_node;
573 *node = list_node_zeroed;
577 static inline void rect_list_del_next(list_t *rects, list_node_t *parent_node)
581 node = rect_list_unlink_next(rects, parent_node);
582 rect_list_node_pool_put(node);
585 static void rect_list_clear(list_t *rects)
595 rect_list_node_pool_put(node);
598 *rects = list_zeroed;
601 static void rect_list_del_split_strict(list_t *rects, const rect_t del_r)
603 list_t modified = list_zeroed;
604 list_node_t *cur_node, *prev_node;
607 cur_node = rects->head;
610 int intra_width, intra_height;
613 current = ((rect_node_t *)cur_node)->rect;
615 _calc_intra_rect_area(del_r, current, &intra_width,
617 if ((intra_width <= 0) || (intra_height <= 0))
619 /* .---.current .---.del_r
621 * `---+---.del_r `---+---.current
624 * no intersection, nothing to do
626 prev_node = cur_node;
627 cur_node = cur_node->next;
629 else if ((intra_width == current.width) && (intra_height
637 * current is contained, remove from rects
639 cur_node = cur_node->next;
640 rect_list_del_next(rects, prev_node);
644 _split_strict(&modified, del_r, current);
645 cur_node = cur_node->next;
646 rect_list_del_next(rects, prev_node);
650 rect_list_concat(rects, &modified);
654 static void rect_list_add_split_strict(list_t *rects, list_node_t *node)
656 list_t dirty = list_zeroed;
657 list_t new_dirty = list_zeroed;
658 list_node_t *cur_node;
662 rect_list_append_node(rects, node);
666 rect_list_append_node(&dirty, node);
668 cur_node = rects->head;
675 rect_list_concat(rects, &dirty);
679 current = ((rect_node_t *)cur_node)->rect;
683 int intra_width, intra_height;
686 r = ((rect_node_t *)dirty.head)->rect;
687 _calc_intra_rect_area(r, current, &intra_width,
689 if ((intra_width == r.width) && (intra_height
697 rect_list_del_next(&dirty, NULL);
698 else if ((intra_width <= 0) || (intra_height <= 0))
702 * `---+---.r `---+---.cur
707 tmp = rect_list_unlink_next(&dirty, NULL);
708 rect_list_append_node(&new_dirty, tmp);
712 _split_strict(&new_dirty, current, r);
713 rect_list_del_next(&dirty, NULL);
717 new_dirty = list_zeroed;
719 cur_node = cur_node->next;
725 rect_list_add_split_fuzzy(list_t *rects, list_node_t *node, int accepted_error)
727 list_t dirty = list_zeroed;
728 list_node_t *old_last;
730 old_last = rects->tail;
734 rect_list_append_node(rects, node);
738 rect_list_append_node(&dirty, node);
741 list_node_t *d_node, *cur_node, *prev_cur_node;
745 d_node = rect_list_unlink_next(&dirty, NULL);
746 r = ((rect_node_t *)d_node)->rect;
748 prev_cur_node = NULL;
749 cur_node = rects->head;
754 rect_t current, intra, outer;
756 current = ((rect_node_t *)cur_node)->rect;
758 _calc_intra_outer_rect_area(r, current, &intra, &outer);
759 area = current.area + r.area - intra.area;
761 if ((intra.width == r.width) && (intra.height
773 else if ((intra.width == current.width)
774 && (intra.height == current.height))
782 if (old_last == cur_node)
783 old_last = prev_cur_node;
785 cur_node = cur_node->next;
786 rect_list_del_next(rects, prev_cur_node);
788 else if ((outer.area - area) <= accepted_error)
790 /* .-----------. bounding box (outer)
796 * merge them, remove both and add merged
800 if (old_last == cur_node)
801 old_last = prev_cur_node;
803 n = (rect_node_t *)rect_list_unlink_next(
804 rects, prev_cur_node);
806 rect_list_append_node(&dirty, (list_node_t *)n);
811 else if (intra.area <= accepted_error)
815 * `---+---.r `---+---.cur
820 prev_cur_node = cur_node;
821 cur_node = cur_node->next;
825 /* split is required */
826 action = _split_fuzzy(&dirty, current, &r);
827 if (action == SPLIT_FUZZY_ACTION_MERGE)
829 /* horizontal merge is possible: remove both, add merged */
832 if (old_last == cur_node)
833 old_last = prev_cur_node;
836 = (rect_node_t *)rect_list_unlink_next(
840 n->rect.left = outer.left;
841 n->rect.width = outer.width;
842 n->rect.right = outer.right;
843 n->rect.area = outer.width * r.height;
844 rect_list_append_node(&dirty,
847 else if (action == SPLIT_FUZZY_ACTION_NONE)
850 * this rect check was totally useless,
851 * should never happen
853 /* prev_cur_node = cur_node; */
854 /* cur_node = cur_node->next; */
855 printf("Should not get here!\n");
863 if (EINA_UNLIKELY(keep_dirty))
864 rect_list_append_node(rects, d_node);
866 rect_list_node_pool_put(d_node);
872 static inline void _calc_outer_rect_area(const rect_t a, const rect_t b,
875 int min_left, max_right;
876 int min_top, max_bottom;
883 if (a.right < b.right)
888 outer->left = min_left;
889 outer->right = max_right;
890 outer->width = max_right - min_left;
897 if (a.bottom < b.bottom)
898 max_bottom = b.bottom;
900 max_bottom = a.bottom;
902 outer->top = min_top;
903 outer->bottom = max_bottom;
904 outer->height = max_bottom - min_top;
906 outer->area = outer->width * outer->height;
909 static void rect_list_merge_rects(list_t *rects,
913 while (to_merge->head)
915 list_node_t *node, *parent_node;
919 r1 = ((rect_node_t *)to_merge->head)->rect;
929 r2 = ((rect_node_t *)node)->rect;
931 _calc_outer_rect_area(r1, r2, &outer);
932 area = r1.area + r2.area; /* intra area is taken as 0 */
933 if (outer.area - area <= accepted_error)
936 * remove both r1 and r2, create r3
937 * actually r3 uses r2 instance, saves memory
941 n = (rect_node_t *)rect_list_unlink_next(
944 rect_list_append_node(to_merge,
957 n = rect_list_unlink_next(to_merge, NULL);
958 rect_list_append_node(rects, n);
961 rect_list_del_next(to_merge, NULL);
965 static void rect_list_add_split_fuzzy_and_merge(list_t *rects,
967 int split_accepted_error,
968 int merge_accepted_error)
972 n = rect_list_add_split_fuzzy(rects, node, split_accepted_error);
977 /* split list into 2 segments, already merged and to merge */
978 to_merge.head = n->next;
979 to_merge.tail = rects->tail;
983 rect_list_merge_rects(rects, &to_merge, merge_accepted_error);
987 static inline void _splitter_new(Eina_Tiler *t)
989 t->splitter.rects = list_zeroed;
990 t->splitter.need_merge = EINA_FALSE;
993 static inline void _splitter_del(Eina_Tiler *t)
995 rect_list_clear(&t->splitter.rects);
996 rect_list_node_pool_flush();
999 static inline void _splitter_tile_size_set(Eina_Tiler *t,
1003 /* TODO are w and h used for something? */
1004 t->splitter.rects = list_zeroed;
1007 static inline Eina_Bool _splitter_rect_add(Eina_Tiler *t, Eina_Rectangle *rect)
1011 //printf("ACCOUNTING[1]: add_redraw: %4d,%4d %3dx%3d\n", x, y, w, h);
1019 rn = (rect_node_t *)rect_list_node_pool_get();
1020 rn->_lst = list_node_zeroed;
1021 rect_init(&rn->rect, rect->x, rect->y, rect->w, rect->h);
1022 //printf("ACCOUNTING[2]: add_redraw: %4d,%4d %3dx%3d\n", x, y, w, h);
1023 //testing on my core2 duo desktop - fuzz of 32 or 48 is best.
1025 rect_list_add_split_fuzzy_and_merge(&t->splitter.rects,
1032 static inline void _splitter_rect_del(Eina_Tiler *t, Eina_Rectangle *rect)
1036 if (!t->splitter.rects.head)
1048 if ((rect->w <= 0) || (rect->h <= 0))
1051 rect_init(&r, rect->x, rect->y, rect->w, rect->h);
1052 //fprintf(stderr, "ACCOUNTING: del_redraw: %4d,%4d %3dx%3d\n", x, y, w, h);
1054 rect_list_del_split_strict(&t->splitter.rects, r);
1055 t->splitter.need_merge = EINA_TRUE;
1059 static inline void _splitter_clear(Eina_Tiler *t)
1061 rect_list_clear(&t->splitter.rects);
1062 t->splitter.need_merge = EINA_FALSE;
1064 /* end of splitter algorithm */
1066 static Eina_Bool _iterator_next(Eina_Iterator_Tiler *it, void **data)
1070 for (n = it->curr; n; n = n->next)
1074 cur = ((rect_node_t *)n)->rect;
1076 it->r.x = cur.left << 1;
1077 it->r.y = cur.top << 1;
1078 it->r.w = cur.width << 1;
1079 it->r.h = cur.height << 1;
1081 if (eina_rectangle_intersection(&it->r, &it->tiler->area) == EINA_FALSE)
1084 if ((it->r.w <= 0) || (it->r.h <= 0))
1088 *(Eina_Rectangle **)data = &it->r;
1094 static void *_iterator_get_container(Eina_Iterator_Tiler *it)
1096 EINA_MAGIC_CHECK_TILER_ITERATOR(it, NULL);
1097 return (void *)it->tiler;
1100 static void _iterator_free(Eina_Iterator_Tiler *it)
1102 EINA_MAGIC_CHECK_TILER_ITERATOR(it);
1106 /*============================================================================*
1108 *============================================================================*/
1110 /*============================================================================*
1112 *============================================================================*/
1114 EAPI Eina_Tiler *eina_tiler_new(int w, int h)
1118 t = calloc(1, sizeof(Eina_Tiler));
1123 EINA_MAGIC_SET(t, EINA_MAGIC_TILER);
1128 EAPI void eina_tiler_free(Eina_Tiler *t)
1133 EINA_MAGIC_CHECK_TILER(t);
1138 EAPI void eina_tiler_tile_size_set(Eina_Tiler *t, int w, int h)
1140 EINA_MAGIC_CHECK_TILER(t);
1141 if ((w <= 0) || (h <= 0))
1146 _splitter_tile_size_set(t, w, h);
1149 EAPI Eina_Bool eina_tiler_rect_add(Eina_Tiler *t, const Eina_Rectangle *r)
1153 EINA_MAGIC_CHECK_TILER(t, EINA_FALSE);
1154 if ((r->w <= 0) || (r->h <= 0))
1158 if (eina_rectangle_intersection(&tmp, &t->area) == EINA_FALSE)
1161 if ((tmp.w <= 0) || (tmp.h <= 0))
1164 return _splitter_rect_add(t, &tmp);
1167 EAPI void eina_tiler_rect_del(Eina_Tiler *t, const Eina_Rectangle *r)
1171 EINA_MAGIC_CHECK_TILER(t);
1172 if ((r->w <= 0) || (r->h <= 0))
1176 if (eina_rectangle_intersection(&tmp, &t->area) == EINA_FALSE)
1179 if ((tmp.w <= 0) || (tmp.h <= 0))
1182 _splitter_rect_del(t, &tmp);
1185 EAPI void eina_tiler_clear(Eina_Tiler *t)
1187 EINA_MAGIC_CHECK_TILER(t);
1192 EAPI Eina_Iterator *eina_tiler_iterator_new(const Eina_Tiler *t)
1194 Eina_Iterator_Tiler *it;
1196 EINA_MAGIC_CHECK_TILER(t, NULL);
1198 it = calloc(1, sizeof (Eina_Iterator_Tiler));
1204 if (t->splitter.need_merge == EINA_TRUE)
1209 sp = (splitter_t *)&(t->splitter);
1210 to_merge = t->splitter.rects;
1211 sp->rects = list_zeroed;
1212 rect_list_merge_rects(&sp->rects, &to_merge, FUZZ * FUZZ);
1216 it->curr = it->tiler->splitter.rects.head;
1218 it->iterator.version = EINA_ITERATOR_VERSION;
1219 it->iterator.next = FUNC_ITERATOR_NEXT(_iterator_next);
1220 it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
1221 _iterator_get_container);
1222 it->iterator.free = FUNC_ITERATOR_FREE(_iterator_free);
1224 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1225 EINA_MAGIC_SET(it, EINA_MAGIC_TILER_ITERATOR);
1227 return &it->iterator;
1230 struct _Eina_Tile_Grid_Slicer_Iterator
1232 Eina_Iterator iterator;
1233 Eina_Tile_Grid_Slicer priv;
1236 typedef struct _Eina_Tile_Grid_Slicer_Iterator Eina_Tile_Grid_Slicer_Iterator;
1239 eina_tile_grid_slicer_iterator_free(Eina_Tile_Grid_Slicer_Iterator *it)
1241 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_NONE);
1246 eina_tile_grid_slicer_iterator_next(Eina_Tile_Grid_Slicer_Iterator *it,
1249 return eina_tile_grid_slicer_next
1250 (&it->priv, (const Eina_Tile_Grid_Info **)data);
1253 EAPI Eina_Iterator *
1254 eina_tile_grid_slicer_iterator_new(int x,
1261 Eina_Tile_Grid_Slicer_Iterator *it;
1263 it = calloc(1, sizeof(*it));
1266 eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
1270 EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1272 it->iterator.version = EINA_ITERATOR_VERSION;
1273 it->iterator.next = FUNC_ITERATOR_NEXT(eina_tile_grid_slicer_iterator_next);
1274 it->iterator.free = FUNC_ITERATOR_FREE(eina_tile_grid_slicer_iterator_free);
1276 eina_tile_grid_slicer_setup(&it->priv, x, y, w, h, tile_w, tile_h);
1278 return &it->iterator;