1324 lines
30 KiB
C
1324 lines
30 KiB
C
#include "evas_common.h"
|
|
#ifdef EVAS_RECT_SPLIT
|
|
|
|
static const list_node_t list_node_zeroed = { NULL };
|
|
static const list_t list_zeroed = { NULL, NULL };
|
|
|
|
typedef struct list_node_pool
|
|
{
|
|
list_node_t *node;
|
|
int len;
|
|
int max;
|
|
} list_node_pool_t;
|
|
|
|
static list_node_pool_t list_node_pool = { NULL, 0, 1024 };
|
|
|
|
void
|
|
rect_list_node_pool_set_max(int max)
|
|
{
|
|
int diff;
|
|
|
|
diff = list_node_pool.len - max;
|
|
for (; diff > 0 && list_node_pool.node; 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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void
|
|
rect_print(const rect_t r)
|
|
{
|
|
INF("<rect(%d, %d, %d, %d)>", r.left, r.top, r.width, r.height);
|
|
}
|
|
|
|
void
|
|
rect_list_print(const list_t rects)
|
|
{
|
|
list_node_t *node;
|
|
int len;
|
|
|
|
len = 0;
|
|
for (node = rects.head; node; node = node->next) len++;
|
|
|
|
putchar('[');
|
|
for (node = rects.head; node; node = node->next)
|
|
{
|
|
rect_print(((rect_node_t *)node)->rect);
|
|
if (node->next)
|
|
{
|
|
putchar(',');
|
|
if (len < 4) putchar(' ');
|
|
else
|
|
{
|
|
putchar('\n');
|
|
putchar(' ');
|
|
}
|
|
}
|
|
}
|
|
putchar(']');
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 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; */
|
|
}
|
|
}
|
|
|
|
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 interception, 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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; */
|
|
WRN("Should not get here!");
|
|
abort();
|
|
}
|
|
|
|
keep_dirty = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
#endif /* EVAS_RECT_SPLIT */
|
|
|
|
#define TILE(tb, x, y) ((tb)->tiles.tiles[((y) * (tb)->tiles.w) + (x)])
|
|
|
|
#ifdef RECTUPDATE
|
|
#elif defined(EVAS_RECT_SPLIT)
|
|
#else
|
|
static int tilebuf_x_intersect(Tilebuf *tb, int x, int w, int *x1, int *x2, int *x1_fill, int *x2_fill);
|
|
static int tilebuf_y_intersect(Tilebuf *tb, int y, int h, int *y1, int *y2, int *y1_fill, int *y2_fill);
|
|
static int tilebuf_intersect(int tsize, int tlen, int tnum, int x, int w, int *x1, int *x2, int *x1_fill, int *x2_fill);
|
|
#endif
|
|
static void tilebuf_setup(Tilebuf *tb);
|
|
|
|
EAPI void
|
|
evas_common_tilebuf_init(void)
|
|
{
|
|
}
|
|
|
|
EAPI Tilebuf *
|
|
evas_common_tilebuf_new(int w, int h)
|
|
{
|
|
Tilebuf *tb;
|
|
|
|
tb = calloc(1, sizeof(Tilebuf));
|
|
if (!tb) return NULL;
|
|
|
|
tb->tile_size.w = 8;
|
|
tb->tile_size.h = 8;
|
|
tb->outbuf_w = w;
|
|
tb->outbuf_h = h;
|
|
|
|
return tb;
|
|
}
|
|
|
|
EAPI void
|
|
evas_common_tilebuf_free(Tilebuf *tb)
|
|
{
|
|
#ifdef RECTUPDATE
|
|
evas_common_regionbuf_free(tb->rb);
|
|
#elif defined(EVAS_RECT_SPLIT)
|
|
rect_list_clear(&tb->rects);
|
|
rect_list_node_pool_flush();
|
|
#else
|
|
if (tb->tiles.tiles) free(tb->tiles.tiles);
|
|
#endif
|
|
free(tb);
|
|
}
|
|
|
|
EAPI void
|
|
evas_common_tilebuf_set_tile_size(Tilebuf *tb, int tw, int th)
|
|
{
|
|
tb->tile_size.w = tw;
|
|
tb->tile_size.h = th;
|
|
tilebuf_setup(tb);
|
|
}
|
|
|
|
EAPI void
|
|
evas_common_tilebuf_get_tile_size(Tilebuf *tb, int *tw, int *th)
|
|
{
|
|
if (tw) *tw = tb->tile_size.w;
|
|
if (th) *th = tb->tile_size.h;
|
|
}
|
|
|
|
#ifdef EVAS_RECT_SPLIT
|
|
static inline int
|
|
_add_redraw(list_t *rects, int max_w, int max_h, int x, int y, int w, int h)
|
|
{
|
|
rect_node_t *rn;
|
|
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, max_w, max_h);
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
|
|
x >>= 1;
|
|
y >>= 1;
|
|
w += 2;
|
|
w >>= 1;
|
|
h += 2;
|
|
h >>= 1;
|
|
|
|
rn = (rect_node_t *)rect_list_node_pool_get();
|
|
rn->_lst = list_node_zeroed;
|
|
rect_init(&rn->rect, x, y, w, h);
|
|
//INF("ACCOUNTING: add_redraw: %4d,%4d %3dx%3d", x, y, w, h);
|
|
//testing on my core2 duo desktop - fuzz of 32 or 48 is best.
|
|
#define FUZZ 32
|
|
rect_list_add_split_fuzzy_and_merge(rects, (list_node_t *)rn,
|
|
FUZZ * FUZZ, FUZZ * FUZZ);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
EAPI int
|
|
evas_common_tilebuf_add_redraw(Tilebuf *tb, int x, int y, int w, int h)
|
|
{
|
|
#ifdef RECTUPDATE
|
|
int i;
|
|
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, tb->outbuf_w, tb->outbuf_h);
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
for (i = 0; i < h; i++)
|
|
evas_common_regionbuf_span_add(tb->rb, x, x + w - 1, y + i);
|
|
return 1;
|
|
#elif defined(EVAS_RECT_SPLIT)
|
|
return _add_redraw(&tb->rects, tb->outbuf_w, tb->outbuf_h, x, y, w, h);
|
|
#else
|
|
int tx1, tx2, ty1, ty2, tfx1, tfx2, tfy1, tfy2, xx, yy;
|
|
int num;
|
|
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, tb->outbuf_w, tb->outbuf_h);
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
num = 0;
|
|
/* wipes out any motion vectors in tiles it touches into redraws */
|
|
if (tilebuf_x_intersect(tb, x, w, &tx1, &tx2, &tfx1, &tfx2) &&
|
|
tilebuf_y_intersect(tb, y, h, &ty1, &ty2, &tfy1, &tfy2))
|
|
{
|
|
Tilebuf_Tile *tbt;
|
|
int delta_x;
|
|
int delta_y;
|
|
|
|
tbt = &(TILE(tb, tx1, ty1));
|
|
delta_x = tx2 - tx1 + 1;
|
|
delta_y = ty2 - ty1 + 1;
|
|
for (yy = delta_y; yy > 0; yy--)
|
|
{
|
|
Tilebuf_Tile *tbti;
|
|
|
|
tbti = tbt;
|
|
for (xx = delta_x; xx > 0; xx--)
|
|
{
|
|
tbti->redraw = 1;
|
|
tbti++;
|
|
}
|
|
tbt += tb->tiles.w;
|
|
}
|
|
num = (tx2 - tx1 + 1) * (ty2 - ty1 + 1);
|
|
}
|
|
return num;
|
|
#endif
|
|
}
|
|
|
|
EAPI int
|
|
evas_common_tilebuf_del_redraw(Tilebuf *tb, int x, int y, int w, int h)
|
|
{
|
|
#ifdef RECTUPDATE
|
|
int i;
|
|
|
|
for (i = 0; i < h; i++)
|
|
evas_common_regionbuf_span_del(tb->rb, x, x + w - 1, y + i);
|
|
#elif defined(EVAS_RECT_SPLIT)
|
|
rect_t r;
|
|
|
|
if (!tb->rects.head) return 0;
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, tb->outbuf_w, tb->outbuf_h);
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
|
|
x += 1;
|
|
y += 1;
|
|
x >>= 1;
|
|
y >>= 1;
|
|
w -= 1;
|
|
w >>= 1;
|
|
h -= 1;
|
|
h >>= 1;
|
|
|
|
if ((w <= 0) || (h <= 0)) return 0;
|
|
|
|
rect_init(&r, x, y, w, h);
|
|
//ERR("ACCOUNTING: del_redraw: %4d,%4d %3dx%3d", x, y, w, h);
|
|
|
|
rect_list_del_split_strict(&tb->rects, r);
|
|
tb->need_merge = 1;
|
|
return 0;
|
|
#else
|
|
int tx1, tx2, ty1, ty2, tfx1, tfx2, tfy1, tfy2, xx, yy;
|
|
int num;
|
|
|
|
num = 0;
|
|
/* wipes out any motion vectors in tiles it touches into redraws */
|
|
if (tilebuf_x_intersect(tb, x, w, &tx1, &tx2, &tfx1, &tfx2) &&
|
|
tilebuf_y_intersect(tb, y, h, &ty1, &ty2, &tfy1, &tfy2))
|
|
{
|
|
Tilebuf_Tile *tbt;
|
|
int delta_y;
|
|
int delta_x;
|
|
|
|
if (!tfx1) tx1++;
|
|
if (!tfx2) tx2--;
|
|
if (!tfy1) ty1++;
|
|
if (!tfy2) ty2--;
|
|
|
|
tbt = &(TILE(tb, tx1, ty1));
|
|
delta_x = tx2 - tx1 + 1;
|
|
delta_y = ty2 - ty1 + 1;
|
|
for (yy = delta_y; yy > 0; yy--)
|
|
{
|
|
Tilebuf_Tile *tbti;
|
|
|
|
tbti = tbt;
|
|
for (xx = delta_x; xx > 0; xx--)
|
|
{
|
|
tbti->redraw = 0;
|
|
tbti++;
|
|
}
|
|
tbt += tb->tiles.w;
|
|
}
|
|
num = (tx2 - tx1 + 1) * (ty2 - ty1 + 1);
|
|
}
|
|
return num;
|
|
#endif
|
|
}
|
|
|
|
EAPI int
|
|
evas_common_tilebuf_add_motion_vector(Tilebuf *tb, int x, int y, int w, int h, int dx, int dy, int alpha __UNUSED__)
|
|
{
|
|
#ifdef EVAS_RECT_SPLIT
|
|
list_t lr = list_zeroed;
|
|
int num;
|
|
|
|
num = _add_redraw(&lr, tb->outbuf_w, tb->outbuf_h, x, y, w, h);
|
|
num += _add_redraw(&lr, tb->outbuf_w, tb->outbuf_h, x + dx, y + dy, w, h);
|
|
while (lr.head)
|
|
{
|
|
list_node_t *node = rect_list_unlink_next(&lr, NULL);
|
|
rect_list_add_split_fuzzy_and_merge(&tb->rects, node,
|
|
FUZZ * FUZZ, FUZZ * FUZZ);
|
|
}
|
|
return num;
|
|
#else
|
|
/* FIXME: need to actually implement motion vectors. for now it just */
|
|
/* implements redraws */
|
|
int num;
|
|
|
|
num = evas_common_tilebuf_add_redraw(tb, x, y, w, h);
|
|
num += evas_common_tilebuf_add_redraw(tb, x + dx, y + dy, w, h);
|
|
return num;
|
|
#endif
|
|
}
|
|
|
|
EAPI void
|
|
evas_common_tilebuf_clear(Tilebuf *tb)
|
|
{
|
|
#ifdef RECTUPDATE
|
|
evas_common_regionbuf_clear(tb->rb);
|
|
#elif defined(EVAS_RECT_SPLIT)
|
|
rect_list_clear(&tb->rects);
|
|
tb->need_merge = 0;
|
|
#else
|
|
if (!tb->tiles.tiles) return;
|
|
memset(tb->tiles.tiles, 0, tb->tiles.w * tb->tiles.h * sizeof(Tilebuf_Tile));
|
|
#endif
|
|
}
|
|
|
|
EAPI Tilebuf_Rect *
|
|
evas_common_tilebuf_get_render_rects(Tilebuf *tb)
|
|
{
|
|
#ifdef RECTUPDATE
|
|
return evas_common_regionbuf_rects_get(tb->rb);
|
|
#elif defined(EVAS_RECT_SPLIT)
|
|
list_node_t *n;
|
|
Tilebuf_Rect *rects = NULL;
|
|
|
|
if (tb->need_merge) {
|
|
list_t to_merge;
|
|
to_merge = tb->rects;
|
|
tb->rects = list_zeroed;
|
|
rect_list_merge_rects(&tb->rects, &to_merge, FUZZ * FUZZ);
|
|
tb->need_merge = 0;
|
|
}
|
|
|
|
for (n = tb->rects.head; n; n = n->next) {
|
|
rect_t cur;
|
|
|
|
cur = ((rect_node_t *)n)->rect;
|
|
|
|
cur.left <<= 1;
|
|
cur.top <<= 1;
|
|
cur.width <<= 1;
|
|
cur.height <<= 1;
|
|
|
|
RECTS_CLIP_TO_RECT(cur.left, cur.top, cur.width, cur.height,
|
|
0, 0, tb->outbuf_w, tb->outbuf_h);
|
|
if ((cur.width > 0) && (cur.height > 0))
|
|
{
|
|
Tilebuf_Rect *r;
|
|
|
|
r = malloc(sizeof(Tilebuf_Rect));
|
|
r->x = cur.left;
|
|
r->y = cur.top;
|
|
r->w = cur.width;
|
|
r->h = cur.height;
|
|
|
|
rects = (Tilebuf_Rect *)eina_inlist_append(EINA_INLIST_GET(rects), EINA_INLIST_GET(r));
|
|
}
|
|
}
|
|
return rects;
|
|
|
|
#else
|
|
Tilebuf_Rect *rects = NULL;
|
|
Tilebuf_Tile *tbt;
|
|
int x, y;
|
|
|
|
tbt = &(TILE(tb, 0, 0));
|
|
for (y = 0; y < tb->tiles.h; y++)
|
|
{
|
|
for (x = 0; x < tb->tiles.w; x++, tbt++)
|
|
{
|
|
if (tbt->redraw)
|
|
{
|
|
Tilebuf_Tile *tbti;
|
|
int can_expand_x = 1, can_expand_y = 1;
|
|
Tilebuf_Rect *r = NULL;
|
|
int xx = 0, yy = 0;
|
|
r = malloc(sizeof(Tilebuf_Rect));
|
|
r->_list_data.next = NULL;
|
|
r->_list_data.prev = NULL;
|
|
r->_list_data.last = NULL;
|
|
|
|
/* amalgamate tiles */
|
|
#if 1
|
|
tbti = tbt;
|
|
while (can_expand_x)
|
|
{
|
|
tbti++;
|
|
xx++;
|
|
if ((x + xx) >= tb->tiles.w)
|
|
can_expand_x = 0;
|
|
else if (!(tbti->redraw))
|
|
can_expand_x = 0;
|
|
if (can_expand_x)
|
|
tbti->redraw = 0;
|
|
}
|
|
tbti = tbt;
|
|
while (can_expand_y)
|
|
{
|
|
int i;
|
|
|
|
tbti += tb->tiles.w;
|
|
yy++;
|
|
if ((y + yy) >= tb->tiles.h)
|
|
can_expand_y = 0;
|
|
if (can_expand_y)
|
|
{
|
|
Tilebuf_Tile *tbtj;
|
|
|
|
tbtj = tbti;
|
|
for (i = x; i < x + xx; i++, tbtj++)
|
|
{
|
|
if (!(tbtj->redraw))
|
|
{
|
|
can_expand_y = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (can_expand_y)
|
|
{
|
|
Tilebuf_Tile *tbtj;
|
|
|
|
tbtj = tbti;
|
|
for (i = x; i < x + xx; i++, tbtj++)
|
|
tbtj->redraw = 0;
|
|
}
|
|
}
|
|
tbt->redraw = 0;
|
|
#else
|
|
xx = 1;
|
|
yy = 1;
|
|
#endif
|
|
r->x = x * tb->tile_size.w;
|
|
r->y = y * tb->tile_size.h;
|
|
r->w = (xx) * tb->tile_size.w;
|
|
r->h = (yy) * tb->tile_size.h;
|
|
rects = eina_inlist_append(rects, r);
|
|
x = x + (xx - 1);
|
|
tbt += xx - 1;
|
|
}
|
|
}
|
|
}
|
|
return rects;
|
|
#endif
|
|
}
|
|
|
|
EAPI void
|
|
evas_common_tilebuf_free_render_rects(Tilebuf_Rect *rects)
|
|
{
|
|
while (rects)
|
|
{
|
|
Tilebuf_Rect *r;
|
|
|
|
r = rects;
|
|
rects = (Tilebuf_Rect *)eina_inlist_remove(EINA_INLIST_GET(rects), EINA_INLIST_GET(r));
|
|
free(r);
|
|
}
|
|
}
|
|
|
|
/* need a way of getting rectangles to: blit, re-render */
|
|
|
|
|
|
|
|
|
|
|
|
/* internal usage */
|
|
|
|
static void
|
|
tilebuf_setup(Tilebuf *tb)
|
|
{
|
|
if ((tb->outbuf_w <= 0) || (tb->outbuf_h <= 0)) return;
|
|
#ifdef RECTUPDATE
|
|
tb->rb = evas_common_regionbuf_new(tb->outbuf_w, tb->outbuf_h);
|
|
#elif defined(EVAS_RECT_SPLIT)
|
|
tb->rects = list_zeroed;
|
|
#else
|
|
if (tb->tiles.tiles) free(tb->tiles.tiles);
|
|
tb->tiles.tiles = NULL;
|
|
|
|
tb->tiles.w = (tb->outbuf_w + (tb->tile_size.w - 1)) / tb->tile_size.w;
|
|
tb->tiles.h = (tb->outbuf_h + (tb->tile_size.h - 1)) / tb->tile_size.h;
|
|
|
|
tb->tiles.tiles = malloc(tb->tiles.w * tb->tiles.h * sizeof(Tilebuf_Tile));
|
|
|
|
if (!tb->tiles.tiles)
|
|
{
|
|
tb->tiles.w = 0;
|
|
tb->tiles.h = 0;
|
|
return;
|
|
}
|
|
memset(tb->tiles.tiles, 0, tb->tiles.w * tb->tiles.h * sizeof(Tilebuf_Tile));
|
|
#endif
|
|
}
|
|
|
|
#ifdef RECTUPDATE
|
|
#elif defined(EVAS_RECT_SPLIT)
|
|
#else
|
|
static int
|
|
tilebuf_x_intersect(Tilebuf *tb, int x, int w, int *x1, int *x2, int *x1_fill, int *x2_fill)
|
|
{
|
|
return tilebuf_intersect(tb->tile_size.w, tb->outbuf_w, tb->tiles.w,
|
|
x, w, x1, x2, x1_fill, x2_fill);
|
|
}
|
|
|
|
static int
|
|
tilebuf_y_intersect(Tilebuf *tb, int y, int h, int *y1, int *y2, int *y1_fill, int *y2_fill)
|
|
{
|
|
return tilebuf_intersect(tb->tile_size.h, tb->outbuf_h, tb->tiles.h,
|
|
y, h, y1, y2, y1_fill, y2_fill);
|
|
}
|
|
|
|
static int
|
|
tilebuf_intersect(int tsize, int tlen, int tnum, int x, int w, int *x1, int *x2, int *x1_fill, int *x2_fill)
|
|
{
|
|
int p1, p2;
|
|
|
|
/* initial clip out of region */
|
|
if ((x + w) <= 0) return 0;
|
|
if (x >= tlen) return 0;
|
|
|
|
/* adjust x & w so it all fits in region */
|
|
if (x < 0)
|
|
{
|
|
w += x;
|
|
x = 0;
|
|
}
|
|
if (w < 0) return 0;
|
|
if ((x + w) > tlen) w = tlen - x;
|
|
|
|
/* now figure if the first edge is fully filling its tile */
|
|
p1 = (x) / tsize;
|
|
if ((p1 * tsize) == (x)) *x1_fill = 1;
|
|
else *x1_fill = 0;
|
|
*x1 = p1;
|
|
|
|
/* now figure if the last edge is fully filling its tile */
|
|
p2 = (x + w - 1) / tsize;
|
|
if (((p2 + 1) * tsize) == (x + w)) *x2_fill = 1;
|
|
else *x2_fill = 0;
|
|
*x2 = p2;
|
|
|
|
return 1;
|
|
tnum = 0;
|
|
}
|
|
#endif
|