efl/legacy/eina/src/lib/eina_rectangle.c

339 lines
7.9 KiB
C

/* EINA - EFL data type library
* Copyright (C) 2007-2008 Cedric BAIL, Carsten Haitzler
*
* 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/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include "eina_rectangle.h"
#include "eina_magic.h"
#include "eina_inlist.h"
#include "eina_private.h"
/*============================================================================*
* Local *
*============================================================================*/
/**
* @cond LOCAL
*/
#define EINA_RECTANGLE_POOL_MAGIC 0x1578FCB0
#define EINA_RECTANGLE_ALLOC_MAGIC 0x1578FCB1
typedef struct _Eina_Rectangle_Alloc Eina_Rectangle_Alloc;
struct _Eina_Rectangle_Pool
{
Eina_Inlist *head;
void *data;
unsigned int references;
int w;
int h;
EINA_MAGIC;
};
struct _Eina_Rectangle_Alloc
{
EINA_INLIST;
Eina_Rectangle_Pool *pool;
EINA_MAGIC;
};
#define EINA_MAGIC_CHECK_RECTANGLE_POOL(d) \
do { \
if (!EINA_MAGIC_CHECK((d), EINA_RECTANGLE_POOL_MAGIC)) \
EINA_MAGIC_FAIL((d), EINA_RECTANGLE_POOL_MAGIC); \
} while (0);
#define EINA_MAGIC_CHECK_RECTANGLE_ALLOC(d) \
do { \
if (!EINA_MAGIC_CHECK((d), EINA_RECTANGLE_ALLOC_MAGIC)) \
EINA_MAGIC_FAIL((d), EINA_RECTANGLE_ALLOC_MAGIC); \
} while (0);
static inline Eina_Bool
_eina_rectangle_pool_collide(Eina_Rectangle_Alloc *head, Eina_Rectangle_Alloc *current, Eina_Rectangle *test)
{
Eina_Rectangle_Alloc *collide;
EINA_INLIST_FOREACH(head, collide)
{
Eina_Rectangle *colliding_rect = (Eina_Rectangle*) (collide + 1);
if (collide == current) continue;
if (eina_rectangles_intersect(colliding_rect, test))
return EINA_TRUE;
}
return EINA_FALSE;
}
static Eina_Bool
_eina_rectangle_pool_find(Eina_Rectangle_Alloc *head, int poolw, int poolh, int w, int h, int *x, int *y)
{
Eina_Rectangle_Alloc *item;
Eina_Rectangle tmp = { 0, 0, 0, 0 };
if (head == NULL) goto on_intersect;
EINA_INLIST_FOREACH(head, item)
{
Eina_Rectangle *rect = (Eina_Rectangle*) (item + 1);
Eina_Bool t1 = EINA_TRUE;
Eina_Bool t2 = EINA_TRUE;
Eina_Bool t3 = EINA_TRUE;
Eina_Bool t4 = EINA_TRUE;
Eina_Bool intersects;
if ((rect->x + rect->w + w) > poolw) t1 = EINA_FALSE;
if ((rect->y + h) > poolh) t1 = EINA_FALSE;
if ((rect->y + rect->h + h) > poolh) t2 = EINA_FALSE;
if ((rect->x + w) > poolw) t2 = EINA_FALSE;
if ((rect->x - w) < 0) t3 = EINA_FALSE;
if ((rect->y + h) > poolh) t3 = EINA_FALSE;
if ((rect->x + w) > poolw) t4 = EINA_FALSE;
if ((rect->y - h) < 0) t4 = EINA_FALSE;
intersects = EINA_FALSE;
if (t1)
{
/* 1. try here:
* +----++--+
* |AAAA||??|
* |AAAA|+--+
* |AAAA|
* +----+
*/
eina_rectangle_coords_from(&tmp, rect->x + rect->w, rect->y, w, h);
intersects = _eina_rectangle_pool_collide(head, item, &tmp);
if (!intersects) goto on_intersect;
}
intersects = EINA_FALSE;
if (t2)
{
/* 2. try here:
* +----+
* |AAAA|
* |AAAA|
* |AAAA|
* +----+
* +--+
* |??|
* +--+
*/
eina_rectangle_coords_from(&tmp, rect->x, rect->y + rect->h, w, h);
intersects = _eina_rectangle_pool_collide(head, item, &tmp);
if (!intersects) goto on_intersect;
}
intersects = EINA_FALSE;
if (t3)
{
/* 3. try here:
* +--++----+
* |??||AAAA|
* +--+|AAAA|
* |AAAA|
* +----+
*/
eina_rectangle_coords_from(&tmp, rect->x - w, rect->y, w, h);
intersects = _eina_rectangle_pool_collide(head, item, &tmp);
if (!intersects) goto on_intersect;
}
intersects = EINA_FALSE;
if (t4)
{
/* 2. try here:
* +--+
* |??|
* +--+
* +----+
* |AAAA|
* |AAAA|
* |AAAA|
* +----+
*/
eina_rectangle_coords_from(&tmp, rect->x, rect->y - h, w, h);
intersects = _eina_rectangle_pool_collide(head, item, &tmp);
if (!intersects) goto on_intersect;
}
}
return EINA_FALSE;
on_intersect:
*x = tmp.x;
*y = tmp.y;
return EINA_TRUE;
}
/**
* @endcond
*/
/*============================================================================*
* Global *
*============================================================================*/
/*============================================================================*
* API *
*============================================================================*/
EAPI Eina_Rectangle_Pool *
eina_rectangle_pool_add(int w, int h)
{
Eina_Rectangle_Pool *new;
new = malloc(sizeof (Eina_Rectangle_Pool));
if (!new) return NULL;
new->head = NULL;
new->references = 0;
new->w = w;
new->h = h;
EINA_MAGIC_SET(new, EINA_RECTANGLE_POOL_MAGIC);
return new;
}
EAPI void
eina_rectangle_pool_delete(Eina_Rectangle_Pool *pool)
{
Eina_Rectangle_Alloc *del;
if (!pool) return ;
while (pool->head)
{
del = (Eina_Rectangle_Alloc*) pool->head;
pool->head = (EINA_INLIST_GET(del))->next;
MAGIC_FREE(del);
}
MAGIC_FREE(pool);
}
EAPI int
eina_rectangle_pool_count(Eina_Rectangle_Pool *pool)
{
if (!pool) return 0;
return pool->references;
}
EAPI Eina_Rectangle *
eina_rectangle_pool_request(Eina_Rectangle_Pool *pool, int w, int h)
{
Eina_Rectangle_Alloc *new;
Eina_Rectangle *rect;
Eina_Bool test;
int x;
int y;
if (!pool) return NULL;
if (w > pool->w || h > pool->h) return NULL;
test = _eina_rectangle_pool_find((Eina_Rectangle_Alloc*) pool->head, pool->w, pool->h, w, h, &x, &y);
if (!test) return NULL;
new = malloc(sizeof (Eina_Rectangle_Alloc) + sizeof (Eina_Rectangle));
if (!new) return NULL;
rect = (Eina_Rectangle*) (new + 1);
eina_rectangle_coords_from(rect, x, y, w, h);
pool->head = eina_inlist_prepend(pool->head, EINA_INLIST_GET(new));
pool->references++;
new->pool = pool;
EINA_MAGIC_SET(new, EINA_RECTANGLE_ALLOC_MAGIC);
return rect;
}
EAPI void
eina_rectangle_pool_release(Eina_Rectangle *rect)
{
Eina_Rectangle_Alloc *era = ((Eina_Rectangle_Alloc *) rect) - 1;
EINA_MAGIC_CHECK_RECTANGLE_ALLOC(era);
EINA_MAGIC_CHECK_RECTANGLE_POOL(era->pool);
era->pool->references--;
era->pool->head = eina_inlist_remove(era->pool->head, EINA_INLIST_GET(era));
MAGIC_FREE(era);
}
EAPI Eina_Rectangle_Pool *
eina_rectangle_pool_get(Eina_Rectangle *rect)
{
Eina_Rectangle_Alloc *era = ((Eina_Rectangle_Alloc *) rect) - 1;
if (!rect) return NULL;
EINA_MAGIC_CHECK_RECTANGLE_ALLOC(era);
EINA_MAGIC_CHECK_RECTANGLE_POOL(era->pool);
return era->pool;
}
EAPI void
eina_rectangle_pool_data_set(Eina_Rectangle_Pool *pool, const void *data)
{
if (!pool) return ;
EINA_MAGIC_CHECK_RECTANGLE_POOL(pool);
pool->data = (void*) data;
}
EAPI void *
eina_rectangle_pool_data_get(Eina_Rectangle_Pool *pool)
{
if (!pool) return NULL;
EINA_MAGIC_CHECK_RECTANGLE_POOL(pool);
return pool->data;
}
EAPI Eina_Bool
eina_rectangle_pool_geometry_get(Eina_Rectangle_Pool *pool, int *w, int *h)
{
if (!pool) return EINA_FALSE;
EINA_MAGIC_CHECK_RECTANGLE_POOL(pool);
if (w) *w = pool->w;
if (h) *h = pool->h;
return EINA_TRUE;
}