/*
Copyright (C) 2009 Red Hat, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include "region.h"
#include "rect.h"
//#define ALLOC_ON_STEAL
//#define REGION_DEBUG
#define FALSE 0
#define TRUE 1
#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
#define ASSERT(x) if (!(x)) { \
printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
abort(); \
}
#ifdef REGION_DEBUG
#define REGION_IS_VALID(region) region_is_valid(region)
#else
#define REGION_IS_VALID(region) TRUE
#endif
static int rect_is_valid(const Rect *r)
{
if (r->top > r->bottom || r->left > r->right) {
printf("%s: invalid rect\n", __FUNCTION__);
return FALSE;
}
return TRUE;
}
#ifdef REGION_TEST
static void rect_set(Rect *r, int32_t top, int32_t left, int32_t bottom, int32_t right)
{
r->top = top;
r->left = left;
r->bottom = bottom;
r->right = right;
ASSERT(rect_is_valid(r));
}
#endif
static inline void __region_init(QRegion *rgn)
{
rgn->num_rects = 0;
rgn->rects = rgn->buf;
rgn->rects_size = RECTS_BUF_SIZE;
}
void region_init(QRegion *rgn)
{
__region_init(rgn);
ASSERT(REGION_IS_VALID(rgn));
}
void region_clear(QRegion *rgn)
{
rgn->num_rects = 0;
}
void region_destroy(QRegion *rgn)
{
ASSERT(REGION_IS_VALID(rgn));
if (rgn->rects != rgn->buf) {
free(rgn->rects);
}
}
void region_clone(QRegion *dest, const QRegion *src)
{
ASSERT(REGION_IS_VALID(src));
dest->bbox = src->bbox;
if ((dest->num_rects = src->num_rects) <= RECTS_BUF_SIZE) {
dest->rects = dest->buf;
dest->rects_size = RECTS_BUF_SIZE;
} else {
dest->rects = (Rect *)malloc(sizeof(Rect) * dest->num_rects);
dest->rects_size = dest->num_rects;
}
memcpy(dest->rects, src->rects, dest->num_rects * sizeof(Rect));
ASSERT(REGION_IS_VALID(src));
ASSERT(REGION_IS_VALID(dest));
}
int region_is_valid(const QRegion *rgn)
{
if (rgn->num_rects) {
uint32_t i;
Rect bbox;
if (!rect_is_valid(&rgn->bbox)) {
return FALSE;
}
bbox = rgn->rects[0];
if (!rect_is_valid(&bbox) || rect_is_empty(&bbox)) {
return FALSE;
}
for (i = 1; i < rgn->num_rects; i++) {
Rect *r;
r = &rgn->rects[i];
if (!rect_is_valid(r) || rect_is_empty(r)) {
return FALSE;
}
Rect *priv = r - 1;
if (r->top < priv->top) {
return FALSE;
} else if (r->top == priv->top) {
if (r->bottom != priv->bottom) {
return FALSE;
}
if (r->left < priv->right) {
return FALSE;
}
} else if (priv->bottom > r->top) {
return FALSE;
}
bbox.top = MIN(bbox.top, r->top);
bbox.left = MIN(bbox.left, r->left);
bbox.bottom = MAX(bbox.bottom, r->bottom);
bbox.right = MAX(bbox.right, r->right);
}
return rect_is_equal(&bbox, &rgn->bbox);
}
return TRUE;
}
void region_dump(const QRegion *rgn, const char *prefix)
{
char *indent;
int len;
uint32_t i;
len = strlen(prefix);
if (!(indent = (char *)malloc(len + 1))) {
printf("%s: malloc failed\n", __FUNCTION__);
return;
}
memset(indent, ' ', len);
indent[len] = 0;
printf("%sREGION: %p, size %u storage is %s, ",
prefix,
rgn,
rgn->rects_size,
(rgn->rects == rgn->buf) ? "BUF" : "MALLOC");
if (rgn->num_rects == 0) {
printf("EMPTY\n");
return;
}
printf("num %u bounds (%d, %d, %d, %d)\n",
rgn->num_rects,
rgn->bbox.top,
rgn->bbox.left,
rgn->bbox.bottom,
rgn->bbox.right);
for (i = 0; i < rgn->num_rects; i++) {
printf("%s %12d %12d %12d %12d\n",
indent,
rgn->rects[i].top,
rgn->rects[i].left,
rgn->rects[i].bottom,
rgn->rects[i].right);
}
free(indent);
ASSERT(region_is_valid(rgn));
}
int region_is_empty(const QRegion *rgn)
{
ASSERT(REGION_IS_VALID(rgn));
return !rgn->num_rects;
}
#ifdef REGION_USE_IMPROVED
int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
{
int test_res;
ASSERT(REGION_IS_VALID(rgn1));
ASSERT(REGION_IS_VALID(rgn2));
if (rgn1->num_rects == 0 || rgn2->num_rects == 0) {
return rgn1->num_rects == rgn2->num_rects;
}
if (!rect_is_equal(&rgn1->bbox, &rgn2->bbox)) {
return FALSE;
}
test_res = region_test(rgn1, rgn2, REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE);
return !test_res;
}
#else
int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
{
QRegion tmp_rgn;
int ret;
ASSERT(REGION_IS_VALID(rgn1));
ASSERT(REGION_IS_VALID(rgn2));
if (rgn1->num_rects == 0 || rgn2->num_rects == 0) {
return rgn1->num_rects == rgn2->num_rects;
}
if (!rect_is_equal(&rgn1->bbox, &rgn2->bbox)) {
return FALSE;
}
region_clone(&tmp_rgn, rgn1);
region_xor(&tmp_rgn, rgn2);
ret = region_is_empty(&tmp_rgn);
region_destroy(&tmp_rgn);
return ret;
}
#endif
typedef struct RgnOpCtx {
Rect *now;
Rect *end;
Rect *scan_line;
Rect r;
Rect split;
#ifdef REGION_USE_IMPROVED
int abort;
#endif
} RgnOpCtx;
static inline int op_ctx_is_valid(RgnOpCtx *ctx)
{
return ctx->now != ctx->end;
}
static void op_context_next(RgnOpCtx *ctx)
{
Rect *now;
Rect *next;
ASSERT(op_ctx_is_valid(ctx));
now = ctx->now;
next = now + 1;
if (next == ctx->end || now->top != next->top) {
if (now->bottom != ctx->r.bottom) { //h_split
ctx->r.top = ctx->r.bottom;
ctx->r.bottom = now->bottom;
next = ctx->scan_line;
} else {
if (next == ctx->end) {
#ifdef REGION_USE_IMPROVED
ctx->scan_line = ++ctx->now;
#else
++ctx->now;
#endif
ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
return;
}
ctx->scan_line = next;
ctx->r.top = next->top;
ctx->r.bottom = next->bottom;
}
}
ctx->r.left = next->left;
ctx->r.right = next->right;
ctx->now = next;
}
static void op_context_init(RgnOpCtx *ctx, uint32_t num_rects, Rect *rects)
{
ctx->scan_line = ctx->now = rects;
ctx->end = ctx->now + num_rects;
#ifdef REGION_USE_IMPROVED
ctx->abort = FALSE;
#endif
if (!op_ctx_is_valid(ctx)) {
ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
} else {
ctx->r = *ctx->now;
}
}
static inline void op_ctx_h_split(RgnOpCtx *ctx, int32_t h_line)
{
ctx->r.bottom = h_line;
ctx->split = ctx->r;
op_context_next(ctx);
}
static inline void op_ctx_v_split(RgnOpCtx *ctx, int32_t v_line)
{
ctx->split = ctx->r;
ctx->r.left = ctx->split.right = v_line;
if (rect_is_empty(&ctx->r)) {
op_context_next(ctx);
}
}
static inline void op_ctx_split(RgnOpCtx *ctx, int32_t h_line)
{
ASSERT(ctx->now == ctx->scan_line);
ctx->r.bottom = h_line;
}
static void region_steal_rects(QRegion *rgn, uint32_t *num_rects, Rect **rects)
{
ASSERT(REGION_IS_VALID(rgn));
if ((*num_rects = rgn->num_rects)) {
if (rgn->rects == rgn->buf) {
*rects = (Rect *)malloc(sizeof(Rect) * rgn->num_rects);
memcpy(*rects, rgn->rects, sizeof(Rect) * rgn->num_rects);
} else {
*rects = rgn->rects;
#ifdef ALLOC_ON_STEAL
rgn->rects = (Rect *)malloc(sizeof(Rect) * rgn->num_rects);
rgn->rects_size = rgn->num_rects;
rgn->num_rects = 0;
return;
#endif
}
} else {
*rects = NULL;
}
__region_init(rgn);
ASSERT(REGION_IS_VALID(rgn));
}
typedef struct JoinContext {
QRegion *rgn;
Rect *line0;
Rect *line1;
Rect *end;
} JoinContext;
static inline Rect *__get_line(QRegion *rgn, Rect *pos)
{
Rect *end = rgn->rects + rgn->num_rects;
if (pos < end) {
int32_t line_top = pos->top;
while (++pos < end && pos->top == line_top) {
ASSERT((pos - 1)->right < pos->left); //join in region_push_rect
}
}
return pos;
}
static inline int region_join_init(QRegion *rgn, JoinContext *context)
{
context->rgn = rgn;
context->end = __get_line(rgn, (context->line1 = rgn->rects));
return context->end != context->line1;
}
static inline int region_join_next(JoinContext *context)
{
context->line0 = context->line1;
context->line1 = context->end;
context->end = __get_line(context->rgn, context->line1);
return context->end != context->line1;
}
static inline void region_join_join(JoinContext *context)
{
Rect *pos_0 = context->line0;
Rect *pos_1 = context->line1;
int32_t bottom;
QRegion *rgn;
if (pos_0->bottom != pos_1->top) {
return;
}
if (pos_1 - pos_0 != context->end - pos_1) {
return;
}
for (; pos_1 < context->end; pos_0++, pos_1++) {
if (pos_0->left != pos_1->left || pos_0->right != pos_1->right) {
return;
}
}
bottom = context->line1->bottom;
pos_0 = context->line0;
for (; pos_0 < context->line1; pos_0++) {
pos_0->bottom = bottom;
}
rgn = context->rgn;
memmove(context->line1, context->end,
(unsigned long)(rgn->rects + rgn->num_rects) - (unsigned long)context->end);
rgn->num_rects -= (context->line1 - context->line0);
context->end = context->line1;
context->line1 = context->line0;
}
static inline void region_join(QRegion *rgn)
{
JoinContext context;
ASSERT(REGION_IS_VALID(rgn));
if (!region_join_init(rgn, &context)) {
return;
}
while (region_join_next(&context)) {
region_join_join(&context);
}
ASSERT(REGION_IS_VALID(rgn));
}
static void region_push_rect(QRegion *rgn, Rect *r)
{
ASSERT(REGION_IS_VALID(rgn));
ASSERT(rect_is_valid(r));
if (rgn->num_rects == 0) {
rgn->num_rects++;
rgn->rects[0] = rgn->bbox = *r;
return;
} else {
Rect *priv = &rgn->rects[rgn->num_rects - 1];
if (priv->top == r->top && priv->right == r->left) {
ASSERT(priv->bottom == r->bottom);
priv->right = r->right;
rgn->bbox.right = MAX(rgn->bbox.right, priv->right);
return;
}
if (rgn->rects_size == rgn->num_rects) {
Rect *old = rgn->rects;
rgn->rects_size = rgn->rects_size * 2;
rgn->rects = (Rect *)malloc(sizeof(Rect) * rgn->rects_size);
memcpy(rgn->rects, old, sizeof(Rect) * rgn->num_rects);
if (old != rgn->buf) {
free(old);
}
}
rgn->rects[rgn->num_rects++] = *r;
rect_union(&rgn->bbox, r);
}
}
#ifdef REGION_USE_IMPROVED
static Rect *op_context_find_area_below(RgnOpCtx *ctx, int32_t val)
{
Rect *start = ctx->now;
Rect *end = ctx->end;
while (start != end) {
int pos = (end - start) / 2;
if (start[pos].bottom <= val) {
start = &start[pos + 1];
} else {
end = &start[pos];
}
}
return start;
}
static int op_context_skip_v(RgnOpCtx *ctx, int32_t top)
{
Rect *end = op_context_find_area_below(ctx, top);
if (end != ctx->now) {
ctx->now = ctx->scan_line = end;
if (ctx->now == ctx->end) {
ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
} else {
ctx->r = *ctx->now;
}
return TRUE;
}
return FALSE;
}
typedef void (*op_callback_t)(RgnOpCtx *context, Rect *, Rect *);
static void op_context_skip(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
op_callback_t on_other)
{
Rect *save1 = self->now;
Rect *save2 = other->now;
int more;
do {
op_context_skip_v(self, other->r.top);
if (save1 != self->now) {
if (on_self) {
on_self(self, save1, self->now);
}
save1 = self->now;
}
more = op_context_skip_v(other, self->r.top);
if (save2 != other->now) {
if (on_other) {
on_other(self, save2, other->now);
}
save2 = other->now;
}
} while (more && !self->abort);
}
static inline int op_context_more_overlap(RgnOpCtx *ctx, int32_t *bottom)
{
if (!op_ctx_is_valid(ctx)) {
return FALSE;
}
if (ctx->scan_line->bottom > *bottom && ctx->scan_line->top < *bottom) {
*bottom = ctx->scan_line->bottom;
}
return ctx->scan_line->top < *bottom;
}
static inline void op_context_overlap(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
op_callback_t on_other, op_callback_t on_both)
{
int32_t bottom = MAX(self->scan_line->bottom, other->scan_line->bottom);
do {
if (self->r.top < other->r.top) {
op_ctx_h_split(self, MIN(other->r.top, self->r.bottom));
if (on_self) {
on_self(self, &self->split, &self->split + 1);
}
} else if (self->r.top > other->r.top) {
op_ctx_h_split(other, MIN(self->r.top, other->r.bottom));
if (on_other) {
on_other(self, &other->split, &other->split + 1);
}
} else {
if (self->r.bottom > other->r.bottom) {
op_ctx_split(self, other->r.bottom);
} else if (other->r.bottom > self->r.bottom) {
op_ctx_split(other, self->r.bottom);
}
if (self->r.left < other->r.left) {
op_ctx_v_split(self, MIN(other->r.left, self->r.right));
if (on_self) {
on_self(self, &self->split, &self->split + 1);
}
} else if (self->r.left > other->r.left) {
op_ctx_v_split(other, MIN(self->r.left, other->r.right));
if (on_other) {
on_other(self, &other->split, &other->split + 1);
}
} else {
int32_t right = MIN(self->r.right, other->r.right);
op_ctx_v_split(self, right);
op_ctx_v_split(other, right);
if (on_both) {
on_both(self, &self->split, &self->split + 1);
}
}
}
} while (!self->abort && (op_context_more_overlap(self, &bottom) ||
op_context_more_overlap(other, &bottom)));
}
static inline void op_context_op(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
op_callback_t on_other, op_callback_t on_both)
{
for (;;) {
op_context_skip(self, other, on_self, on_other);
if (self->abort || !op_ctx_is_valid(self)) {
ASSERT(self->abort || !op_ctx_is_valid(other));
return;
}
op_context_overlap(self, other, on_self, on_other, on_both);
}
}
typedef struct SelfOpCtx {
RgnOpCtx ctx;
QRegion *rgn;
} SelfOpCtx;
static void add_rects(RgnOpCtx *ctx, Rect *now, Rect *end)
{
SelfOpCtx *self_ctx = (SelfOpCtx *)ctx;
for (; now < end; now++) {
region_push_rect(self_ctx->rgn, now);
}
}
static void region_op(QRegion *rgn, const QRegion *other_rgn, op_callback_t on_self,
op_callback_t on_other, op_callback_t on_both)
{
SelfOpCtx self;
RgnOpCtx other;
uint32_t num_rects;
Rect *rects;
region_steal_rects(rgn, &num_rects, &rects);
op_context_init(&self.ctx, num_rects, rects);
self.rgn = rgn;
op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
op_context_op(&self.ctx, &other, on_self, on_other, on_both);
free(rects);
region_join(rgn);
}
void region_or(QRegion *rgn, const QRegion *other_rgn)
{
region_op(rgn, other_rgn, add_rects, add_rects, add_rects);
}
void region_and(QRegion *rgn, const QRegion *other_rgn)
{
if (!region_bounds_intersects(rgn, other_rgn)) {
region_clear(rgn);
return;
}
region_op(rgn, other_rgn, NULL, NULL, add_rects);
}
void region_xor(QRegion *rgn, const QRegion *other_rgn)
{
region_op(rgn, other_rgn, add_rects, add_rects, NULL);
}
void region_exclude(QRegion *rgn, const QRegion *other_rgn)
{
if (!region_bounds_intersects(rgn, other_rgn)) {
return;
}
region_op(rgn, other_rgn, add_rects, NULL, NULL);
}
typedef struct TestOpCtx {
RgnOpCtx ctx;
int result;
int abort_on;
} TestOpCtx;
static void region_test_on_self(RgnOpCtx *ctx, Rect *now, Rect *end)
{
TestOpCtx *test_ctx = (TestOpCtx *)ctx;
test_ctx->result |= REGION_TEST_LEFT_EXCLUSIVE;
test_ctx->result &= test_ctx->abort_on;
if (test_ctx->result == test_ctx->abort_on) {
test_ctx->ctx.abort = TRUE;
}
}
static void region_test_on_other(RgnOpCtx *ctx, Rect *now, Rect *end)
{
TestOpCtx *test_ctx = (TestOpCtx *)ctx;
test_ctx->result |= REGION_TEST_RIGHT_EXCLUSIVE;
test_ctx->result &= test_ctx->abort_on;
if (test_ctx->result == test_ctx->abort_on) {
test_ctx->ctx.abort = TRUE;
}
}
static void region_test_on_both(RgnOpCtx *ctx, Rect *now, Rect *end)
{
TestOpCtx *test_ctx = (TestOpCtx *)ctx;
test_ctx->result |= REGION_TEST_SHARED;
test_ctx->result &= test_ctx->abort_on;
if (test_ctx->result == test_ctx->abort_on) {
test_ctx->ctx.abort = TRUE;
}
}
int region_test(const QRegion *rgn, const QRegion *other_rgn, int query)
{
TestOpCtx self;
RgnOpCtx other;
op_context_init(&self.ctx, rgn->num_rects, rgn->rects);
self.result = 0;
self.abort_on = (query) ? query & REGION_TEST_ALL : REGION_TEST_ALL;
op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
op_context_op(&self.ctx, &other, region_test_on_self, region_test_on_other,
region_test_on_both);
return self.result;
}
#else
#define RIGION_OP_ADD_SELF (1 << 0)
#define RIGION_OP_ADD_OTHER (1 << 1)
#define RIGION_OP_ADD_COMMON (1 << 2)
static inline void region_on_self(QRegion *rgn, Rect *r, uint32_t op)
{
ASSERT(REGION_IS_VALID(rgn));
if (op & RIGION_OP_ADD_SELF) {
region_push_rect(rgn, r);
}
}
static inline void region_on_other(QRegion *rgn, Rect *r, uint32_t op)
{
ASSERT(REGION_IS_VALID(rgn));
if (op & RIGION_OP_ADD_OTHER) {
region_push_rect(rgn, r);
}
}
static inline void region_on_both(QRegion *rgn, Rect *r, uint32_t op)
{
ASSERT(REGION_IS_VALID(rgn));
if (op & RIGION_OP_ADD_COMMON) {
region_push_rect(rgn, r);
}
}
static void region_op(QRegion *rgn, const QRegion *other_rgn, uint32_t op)
{
RgnOpCtx self;
RgnOpCtx other;
uint32_t num_rects;
Rect *rects;
ASSERT(REGION_IS_VALID(rgn));
ASSERT(REGION_IS_VALID(other_rgn));
region_steal_rects(rgn, &num_rects, &rects);
op_context_init(&self, num_rects, rects);
op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
while (op_ctx_is_valid(&self) || op_ctx_is_valid(&other)) {
if (self.r.top < other.r.top) {
op_ctx_h_split(&self, MIN(other.r.top, self.r.bottom));
region_on_self(rgn, &self.split, op);
} else if (self.r.top > other.r.top) {
op_ctx_h_split(&other, MIN(self.r.top, other.r.bottom));
region_on_other(rgn, &other.split, op);
} else {
if (self.r.bottom > other.r.bottom) {
op_ctx_split(&self, other.r.bottom);
} else if (other.r.bottom > self.r.bottom) {
op_ctx_split(&other, self.r.bottom);
}
if (self.r.left < other.r.left) {
op_ctx_v_split(&self, MIN(other.r.left, self.r.right));
region_on_self(rgn, &self.split, op);
} else if (self.r.left > other.r.left) {
op_ctx_v_split(&other, MIN(self.r.left, other.r.right));
region_on_other(rgn, &other.split, op);
} else {
int32_t right = MIN(self.r.right, other.r.right);
op_ctx_v_split(&self, right);
op_ctx_v_split(&other, right);
region_on_both(rgn, &self.split, op);
}
}
}
free(rects);
region_join(rgn);
}
void region_or(QRegion *rgn, const QRegion *other_rgn)
{
region_op(rgn, other_rgn, RIGION_OP_ADD_SELF | RIGION_OP_ADD_OTHER | RIGION_OP_ADD_COMMON);
}
void region_and(QRegion *rgn, const QRegion *other_rgn)
{
region_op(rgn, other_rgn, RIGION_OP_ADD_COMMON);
}
void region_xor(QRegion *rgn, const QRegion *other_rgn)
{
region_op(rgn, other_rgn, RIGION_OP_ADD_SELF | RIGION_OP_ADD_OTHER);
}
void region_exclude(QRegion *rgn, const QRegion *other_rgn)
{
region_op(rgn, other_rgn, RIGION_OP_ADD_SELF);
}
#endif
void region_offset(QRegion *rgn, int32_t dx, int32_t dy)
{
Rect *now;
Rect *end;
ASSERT(REGION_IS_VALID(rgn));
if (region_is_empty(rgn)) {
return;
}
rect_offset(&rgn->bbox, dx, dy);
now = rgn->rects;
end = now + rgn->num_rects;
for (; now < end; now++) {
rect_offset(now, dx, dy);
}
}
void region_add(QRegion *rgn, const Rect *r)
{
ASSERT(REGION_IS_VALID(rgn));
ASSERT(rect_is_valid(r));
if (!rgn->num_rects) {
if (rect_is_empty(r)) {
return;
}
rgn->num_rects++;
rgn->rects[0] = rgn->bbox = *r;
} else {
QRegion rect_rgn;
region_init(&rect_rgn);
region_add(&rect_rgn, r);
region_or(rgn, &rect_rgn);
}
}
void region_remove(QRegion *rgn, const Rect *r)
{
ASSERT(REGION_IS_VALID(rgn));
ASSERT(rect_is_valid(r));
if (rgn->num_rects) {
QRegion rect_rgn;
region_init(&rect_rgn);
region_add(&rect_rgn, r);
region_exclude(rgn, &rect_rgn);
}
}
#ifdef REGION_USE_IMPROVED
int region_intersects(const QRegion *rgn1, const QRegion *rgn2)
{
int test_res;
ASSERT(REGION_IS_VALID(rgn1));
ASSERT(REGION_IS_VALID(rgn2));
if (!region_bounds_intersects(rgn1, rgn2)) {
return FALSE;
}
test_res = region_test(rgn1, rgn2, REGION_TEST_SHARED);
return !!test_res;
}
#else
int region_intersects(const QRegion *rgn1, const QRegion *rgn2)
{
QRegion tmp;
int ret;
ASSERT(REGION_IS_VALID(rgn1));
ASSERT(REGION_IS_VALID(rgn2));
region_clone(&tmp, rgn1);
region_and(&tmp, rgn2);
ret = !region_is_empty(&tmp);
region_destroy(&tmp);
return ret;
}
#endif
int region_bounds_intersects(const QRegion *rgn1, const QRegion *rgn2)
{
return !region_is_empty(rgn1) && !region_is_empty(rgn2) &&
rect_intersects(&rgn1->bbox, &rgn2->bbox);
}
#ifdef REGION_USE_IMPROVED
int region_contains(const QRegion *rgn, const QRegion *other)
{
int test_res;
ASSERT(REGION_IS_VALID(rgn));
ASSERT(REGION_IS_VALID(other));
test_res = region_test(rgn, other, REGION_TEST_RIGHT_EXCLUSIVE);
return !test_res;
}
#else
int region_contains(const QRegion *rgn, const QRegion *other)
{
QRegion tmp;
int ret;
ASSERT(REGION_IS_VALID(rgn));
ASSERT(REGION_IS_VALID(other));
region_clone(&tmp, rgn);
region_and(&tmp, other);
ret = region_is_equal(&tmp, other);
region_destroy(&tmp);
return ret;
}
#endif
int region_contains_point(const QRegion *rgn, int32_t x, int32_t y)
{
if (region_is_empty(rgn)) {
return FALSE;
}
Rect point;
point.left = x;
point.right = point.left + 1;
point.top = y;
point.bottom = point.top + 1;
if (!rect_intersects(&rgn->bbox, &point)) {
return FALSE;
}
Rect* now = rgn->rects;
Rect* end = now + rgn->num_rects;
for (; now < end; now++) {
if (rect_intersects(now, &point)) {
return TRUE;
}
}
return FALSE;
}
#ifdef REGION_TEST
static void test(const QRegion *r1, const QRegion *r2, int *expected)
{
printf("r1 is_empty %s [%s]\n",
region_is_empty(r1) ? "TRUE" : "FALSE",
(region_is_empty(r1) == *(expected++)) ? "OK" : "ERR");
printf("r2 is_empty %s [%s]\n",
region_is_empty(r2) ? "TRUE" : "FALSE",
(region_is_empty(r2) == *(expected++)) ? "OK" : "ERR");
printf("is_equal %s [%s]\n",
region_is_equal(r1, r2) ? "TRUE" : "FALSE",
(region_is_equal(r1, r2) == *(expected++)) ? "OK" : "ERR");
printf("intersects %s [%s]\n",
region_intersects(r1, r2) ? "TRUE" : "FALSE",
(region_intersects(r1, r2) == *(expected++)) ? "OK" : "ERR");
printf("contains %s [%s]\n",
region_contains(r1, r2) ? "TRUE" : "FALSE",
(region_contains(r1, r2) == *(expected++)) ? "OK" : "ERR");
}
enum {
EXPECT_R1_EMPTY,
EXPECT_R2_EMPTY,
EXPECT_EQUAL,
EXPECT_SECT,
EXPECT_CONT,
};
int main(void)
{
QRegion _r1, _r2, _r3;
QRegion *r1 = &_r1;
QRegion *r2 = &_r2;
QRegion *r3 = &_r3;
Rect _r;
Rect *r = &_r;
int expected[5];
region_init(r1);
region_init(r2);
printf("dump r1 empty rgn [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = TRUE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clone(r3, r1);
printf("dump r3 clone rgn [%s]\n", region_is_valid(r3) ? "VALID" : "INVALID");
region_dump(r3, "");
expected[EXPECT_R1_EMPTY] = TRUE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r3, expected);
region_destroy(r3);
printf("\n");
rect_set(r, 0, 0, 100, 100);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r1);
rect_set(r, 0, 0, 0, 0);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = TRUE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
rect_set(r, -100, -100, 0, 0);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r1);
rect_set(r, -100, -100, 100, 100);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r1);
region_clear(r2);
rect_set(r, 100, 100, 200, 200);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
rect_set(r, 300, 300, 400, 400);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
rect_set(r, 500, 500, 600, 600);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = FALSE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, 100, 100, 200, 200);
region_add(r2, r);
rect_set(r, 300, 300, 400, 400);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, 100, 100, 200, 200);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, -2000, -2000, -1000, -1000);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = FALSE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, -2000, -2000, 1000, 1000);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = FALSE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, 150, 150, 175, 175);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, 150, 150, 350, 350);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = FALSE;
test(r1, r2, expected);
printf("\n");
region_and(r2, r1);
printf("dump r2 and r1 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = FALSE;
test(r2, r1, expected);
printf("\n");
region_clone(r3, r1);
printf("dump r3 clone rgn [%s]\n", region_is_valid(r3) ? "VALID" : "INVALID");
region_dump(r3, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = TRUE;
test(r1, r3, expected);
printf("\n");
region_destroy(r3);
region_destroy(r1);
region_destroy(r2);
return 0;
}
#endif