diff options
author | Yaniv Kamay <ykamay@redhat.com> | 2009-09-19 21:25:46 +0300 |
---|---|---|
committer | Yaniv Kamay <ykamay@redhat.com> | 2009-10-14 15:06:41 +0200 |
commit | c1b79eb035fa158fb2ac3bc8e559809611070016 (patch) | |
tree | 3348dd749a700dedf87c9b16fe8be77c62928df8 /common/region.c |
fresh start
Diffstat (limited to 'common/region.c')
-rw-r--r-- | common/region.c | 1230 |
1 files changed, 1230 insertions, 0 deletions
diff --git a/common/region.c b/common/region.c new file mode 100644 index 00000000..d1d7f4a0 --- /dev/null +++ b/common/region.c @@ -0,0 +1,1230 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#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 + |