efl/src/lib/eina/eina_tiler.c

1634 lines
41 KiB
C

/* 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 <http://www.gnu.org/licenses/>.
*/
/* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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("<rect(%d, %d, %d, %d)>", 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;
}