diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:32 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:32 -0800 |
commit | 8b23a6c7e1aee255004dd19098d4c2462b61b849 (patch) | |
tree | 7a4d682ba51f0ff0364c5ca2509f515bdaf96de9 /android/skin/region.c | |
parent | f721e3ac031f892af46f255a47d7f54a91317b30 (diff) | |
download | external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.zip external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.gz external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'android/skin/region.c')
-rw-r--r-- | android/skin/region.c | 1448 |
1 files changed, 1448 insertions, 0 deletions
diff --git a/android/skin/region.c b/android/skin/region.c new file mode 100644 index 0000000..a5f26a5 --- /dev/null +++ b/android/skin/region.c @@ -0,0 +1,1448 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +*/ +#include "android/skin/region.h" +#include <limits.h> +#include <string.h> +#include <stdlib.h> /* malloc/free */ + +/************************************************************************* + ************************************************************************* + **** + **** ASSERTION SUPPORT + **** + **** + ****/ + +#ifdef UNIT_TEST +#include <stdlib.h> +#include <stdio.h> +static void +_rpanic(void) +{ + *((char*)(void*)0) = 1; /* should SEGFAULT */ + /* put a breakpoint here */ + exit(1); +} + +#define RASSERT(cond) \ + ({ if (!(cond)) { fprintf(stderr, "%s:%d:%s: assertion failed: %s", \ + __FILE__, __LINE__, __FUNCTION__, #cond ); _rpanic(); } }) + +#else +#define RASSERT(cond) ((void)0) +#endif + + +/************************************************************************* + ************************************************************************* + **** + **** IMPLEMENTATION DETAILS + **** + **** + ****/ + +/* this implementation of regions encodes the the region's spans with the + following format: + + region ::= yband+ YSENTINEL + yband ::= YTOP YBOTTOM scanline + scanline ::= span+ XSENTINEL + span ::= XLEFT XRIGHT + + XSENTINEL ::= 0x7fff + YSENTINEL := 0x7fff + + all values are sorted in increasing order, which means that: + + - YTOP1 < YBOTTOM1 <= YTOP2 < YBOTTOM2 <= .... < YSENTINEL + - XLEFT1 < XRIGHT1 < XLEFT2 < XRIGHT2 < .... < XSENTINEL + (in a given scanline) +*/ + +/* convenience shortbuts */ +typedef SkinRegionRun Run; +typedef SkinRegion Region; + +#define RUNS_RECT_COUNT 6 /* YTOP YBOT XLEFT XRIGHT XSENTINEL YSENTINEL */ + +#define XSENTINEL SKIN_REGION_SENTINEL +#define YSENTINEL SKIN_REGION_SENTINEL + +#define RUNS_EMPTY ((Run*)(void*)(-1)) +#define RUNS_RECT ((Run*)(void*)(0)) + +static __inline__ int +region_isEmpty( Region* r ) +{ + return r->runs == RUNS_EMPTY; +} + +static __inline__ int +region_isRect( Region* r ) +{ + return r->runs == RUNS_RECT; +} + +static __inline__ int +region_isComplex( Region* r ) +{ + return r->runs != RUNS_EMPTY && r->runs != RUNS_RECT; +} + +/** RunStore: ref-counted storage for runs + **/ + +typedef struct { + int refcount; + int count; +} RunStore; + +static void +runstore_free( RunStore* s ) +{ + free(s); +} + +static RunStore* +runstore_alloc( int count ) +{ + RunStore* s = malloc( sizeof(*s) + sizeof(Run)*count ); + RASSERT(s != NULL); + s->count = count; + s->refcount = 1; + return s; +} + +static RunStore* +runstore_edit( RunStore* s ) +{ + RunStore* s2; + + if (s->refcount == 1) + return s; + + s2 = runstore_alloc( s->count ); + if (s2) { + memcpy( s2, s, sizeof(*s) + s->count*sizeof(Run) ); + s->refcount -= 1; + s2->refcount = 1; + } + return s2; +} + +static void +runstore_unrefp( RunStore* *ps ) +{ + RunStore* s = *ps; + if (s != NULL) { + if (s->refcount <= 0) + runstore_free(s); + *ps = NULL; + } +} + +static RunStore* +runstore_ref( RunStore* s ) +{ + if (s) s->refcount += 1; + return s; +} + +static __inline__ RunStore* +runstore_from_runs( Run* runs ) +{ + RASSERT(runs != RUNS_EMPTY); + RASSERT(runs != RUNS_RECT); + return (RunStore*)runs - 1; +} + +static __inline__ Run* +runstore_to_runs( RunStore* s ) +{ + RASSERT(s != NULL); + return (Run*)(s + 1); +} + +static Run* +region_edit( Region* r ) +{ + RunStore* s; + + RASSERT(region_isComplex(r)); + + s = runstore_from_runs(r->runs); + s = runstore_edit(s); + r->runs = runstore_to_runs(s); + return r->runs; +} + +/** Run parsing + **/ + +static Run* +runs_next_scanline( Run* runs ) +{ + RASSERT(runs[0] != YSENTINEL && runs[1] != YSENTINEL ); + runs += 2; + do { runs += 1; } while (runs[-1] != XSENTINEL); + return runs; +} + +static Run* +runs_find_y( Run* runs, int y ) +{ + do { + int ybot, ytop = runs[0]; + + if (y < ytop) + return NULL; + + ybot = runs[1]; + if (y < ybot) + return runs; + + runs = runs_next_scanline( runs ); + } while (runs[0] != YSENTINEL); + + return NULL; +} + +static void +runs_set_rect( Run* runs, SkinRect* rect ) +{ + runs[0] = rect->pos.y; + runs[1] = rect->pos.y + rect->size.h; + runs[2] = rect->pos.x; + runs[3] = rect->pos.x + rect->size.w; + runs[4] = XSENTINEL; + runs[5] = YSENTINEL; +} + +static Run* +runs_copy_scanline( Run* dst, Run* src ) +{ + RASSERT(src[0] != YSENTINEL); + RASSERT(src[1] != YSENTINEL); + dst[0] = src[0]; + dst[1] = src[1]; + src += 2; + dst += 2; + do { *dst++ = *src++; } while (src[-1] != XSENTINEL); + return dst; +} + +static Run* +runs_copy_scanline_adj( Run* dst, Run* src, int ytop, int ybot ) +{ + Run* runs2 = runs_copy_scanline( dst, src ); + dst[0] = (Run) ytop; + dst[1] = (Run) ybot; + return runs2; +} + +static __inline__ Run* +runs_add_span( Run* dst, int left, int right ) +{ + dst[0] = (Run) left; + dst[1] = (Run) right; + return dst + 2; +} + +static __inline__ int +runs_get_count( Run* runs ) +{ + RunStore* s = runstore_from_runs(runs); + return s->count; +} + + +static void +runs_coalesce_band( Run* *psrc_spans, Run* *pdst_spans, SkinBox* minmax ) +{ + Run* sspan = *psrc_spans; + Run* dspan = *pdst_spans; + int pleft = sspan[0]; + int pright = sspan[1]; + int xleft, xright; + + RASSERT(pleft != XSENTINEL); + RASSERT(pright != XSENTINEL); + RASSERT(pleft < pright); + + if (pleft < minmax->x1) minmax->x1 = pleft; + + sspan += 2; + xleft = sspan[0]; + + while (xleft != XSENTINEL) + { + xright = sspan[1]; + RASSERT(xright != XSENTINEL); + RASSERT(xleft < xright); + + if (xleft == pright) { + pright = xright; + } else { + dspan[0] = (Run) pleft; + dspan[1] = (Run) pright; + dspan += 2; + } + sspan += 2; + xleft = sspan[0]; + } + dspan[0] = (Run) pleft; + dspan[1] = (Run) pright; + dspan[2] = XSENTINEL; + dspan += 3; + sspan += 1; /* skip XSENTINEL */ + + if (pright > minmax->x2) minmax->x2 = pright; + + *psrc_spans = sspan; + *pdst_spans = dspan; +} + + +static int +runs_coalesce( Run* dst, Run* src, SkinBox* minmax ) +{ + Run* prev = NULL; + Run* dst0 = dst; + int ytop = src[0]; + int ybot; + + while (ytop != YSENTINEL) + { + Run* sspan = src + 2; + Run* dspan = dst + 2; + + ybot = src[1]; + RASSERT( ytop < ybot ); + RASSERT( ybot != YSENTINEL ); + RASSERT( src[2] != XSENTINEL ); + + if (ytop < minmax->y1) minmax->y1 = ytop; + if (ybot > minmax->y2) minmax->y2 = ybot; + + dst[0] = (Run) ytop; + dst[1] = (Run) ybot; + + runs_coalesce_band( &sspan, &dspan, minmax ); + + if (prev && prev[1] == dst[0] && (dst-prev) == (dspan-dst) && + !memcmp(prev+2, dst+2, (dspan-dst-2)*sizeof(Run))) + { + /* coalesce two identical bands */ + prev[1] = dst[1]; + } + else + { + prev = dst; + dst = dspan; + } + src = sspan; + ytop = src[0]; + } + dst[0] = YSENTINEL; + return (dst + 1 - dst0); +} + +/************************************************************************* + ************************************************************************* + **** + **** PUBLIC API + **** + ****/ + +void +skin_region_init_empty( SkinRegion* r ) +{ + /* empty region */ + r->bounds.pos.x = r->bounds.pos.y = 0; + r->bounds.size.w = r->bounds.size.h = 0; + r->runs = RUNS_EMPTY; +} + +void +skin_region_init( SkinRegion* r, int x1, int y1, int x2, int y2 ) +{ + if (x1 >= x2 || y1 >= y2) { + skin_region_init_empty(r); + return; + } + r->bounds.pos.x = x1; + r->bounds.pos.y = y1; + r->bounds.size.w = x2 - x1; + r->bounds.size.h = y2 - y1; + r->runs = RUNS_RECT; +} + +void +skin_region_init_rect( SkinRegion* r, SkinRect* rect ) +{ + if (rect == NULL || rect->size.w <= 0 || rect->size.h <= 0) { + skin_region_init_empty(r); + return; + } + r->bounds = rect[0]; + r->runs = RUNS_RECT; +} + +void +skin_region_init_box( SkinRegion* r, SkinBox* box ) +{ + if (box == NULL || box->x1 >= box->x2 || box->y1 >= box->y2) { + skin_region_init_empty(r); + return; + } + r->bounds.pos.x = box->x1; + r->bounds.pos.y = box->y1; + r->bounds.size.w = box->x2 - box->x1; + r->bounds.size.h = box->y2 - box->y1; + r->runs = RUNS_RECT; +} + +void +skin_region_init_copy( SkinRegion* r, SkinRegion* src ) +{ + if (src == NULL || region_isEmpty(src)) + skin_region_init_empty(r); + else { + r[0] = src[0]; + if (region_isComplex(src)) { + RunStore* s = runstore_from_runs(r->runs); + runstore_ref(s); + } + } +} + + +void +skin_region_reset( SkinRegion* r ) +{ + if (r != NULL) { + if (region_isComplex(r)) { + RunStore* s = runstore_from_runs(r->runs); + runstore_unrefp( &s ); + } + skin_region_init_empty(r); + } +} + + + +void +skin_region_copy( SkinRegion* r, SkinRegion* src ) +{ + skin_region_reset(r); + skin_region_init_copy(r, src); +} + + +int +skin_region_equals( SkinRegion* r1, SkinRegion* r2 ) +{ + Run *runs1, *runs2; + RunStore *store1, *store2; + + if (r1 == r2) + return 1; + + if (!skin_rect_equals( &r1->bounds, &r2->bounds )) + return 0; + + runs1 = r1->runs; + runs2 = r2->runs; + + if (runs1 == runs2) /* empties and rects */ + return 1; + + if ( !region_isComplex(r1) || !region_isComplex(r2) ) + return 0; + + store1 = runstore_from_runs(runs1); + store2 = runstore_from_runs(runs2); + + if (store1->count == store2->count && + !memcmp( (char*)runs1, (char*)runs2, store1->count*sizeof(Run) ) ) + return 1; + + return 0; +} + +void +skin_region_translate( SkinRegion* r, int dx, int dy ) +{ + Run* runs; + + if (region_isEmpty(r)) + return; + + skin_rect_translate( &r->bounds, dx, dy ); + if (region_isRect(r)) + return; + + runs = region_edit(r); + while (runs[0] != YSENTINEL) { + int ytop = runs[0]; + int ybot = runs[1]; + + RASSERT(ybot != YSENTINEL); + runs[0] = (Run)(ytop + dy); + runs[1] = (Run)(ybot + dy); + runs += 2; + while (runs[0] != XSENTINEL) { + int xleft = runs[0]; + int xright = runs[1]; + RASSERT(xright != YSENTINEL); + runs[0] = (Run)(xleft + dx); + runs[1] = (Run)(xright + dx); + runs += 2; + } + runs += 1; + } +} + +void +skin_region_get_bounds( SkinRegion* r, SkinRect* bounds ) +{ + if (r != NULL) { + bounds[0] = r->bounds; + } else { + bounds->pos.x = bounds->pos.y = 0; + bounds->size.w = bounds->size.h = 0; + } +} + +int +skin_region_is_empty( SkinRegion* r ) +{ + return region_isEmpty(r); +} + +int +skin_region_is_rect( SkinRegion* r ) +{ + return region_isRect(r); +} + +int +skin_region_is_complex( SkinRegion* r ) +{ + return region_isComplex(r); +} + +void +skin_region_swap( SkinRegion* r, SkinRegion* r2 ) +{ + SkinRegion tmp; + tmp = r[0]; + r[0] = r2[0]; + r2[0] = tmp; +} + + +SkinOverlap +skin_region_contains( SkinRegion* r, int x, int y ) +{ + if (region_isEmpty(r)) + return SKIN_OUTSIDE; + if (region_isRect(r)) { + return skin_rect_contains(&r->bounds,x,y); + } else { + Run* runs = runs_find_y( r->runs, y ); + if (runs != NULL) { + runs += 2; + do { + int xright, xleft = runs[0]; + + if (x < xleft) // also x < xleft == XSENTINEL + break; + xright = runs[1]; + if (xright == XSENTINEL) + break; + if (x < xright) + return SKIN_INSIDE; + runs += 2; + } while (runs[0] != XSENTINEL); + } + return SKIN_OUTSIDE; + } +} + + +SkinOverlap +skin_region_contains_rect( SkinRegion* r, SkinRect* rect ) +{ + SkinRegion r2[1]; + skin_region_init_rect( r2, rect ); + return skin_region_test_intersect( r, r2 ); +} + + +SkinOverlap +skin_region_contains_box( SkinRegion* r, SkinBox* b ) +{ + SkinRegion r2[1]; + + skin_region_init_box( r2, b ); + return skin_region_test_intersect( r, r2 ); +} + + + +#define FLAG_REGION_1 (1 << 0) +#define FLAG_REGION_2 (1 << 1) +#define FLAG_REGION_BOTH (1 << 2) + +SkinOverlap +skin_region_test_intersect( SkinRegion* r1, + SkinRegion* r2 ) +{ + Run *runs1, *runs2; + Run run2_tmp[ RUNS_RECT_COUNT ]; + SkinRect r; + + if (region_isEmpty(r1) || region_isEmpty(r2)) + return SKIN_OUTSIDE; + + if ( !skin_rect_intersect( &r, &r1->bounds, &r2->bounds) ) + return SKIN_OUTSIDE; + + if (region_isRect(r1)) { + if (region_isRect(r2)) { + return skin_rect_contains_rect(&r1->bounds, &r2->bounds); + } else { + SkinRegion* tmp = r1; + r1 = r2; + r2 = tmp; + } + } + /* here r1 is guaranteed to be complex, r2 is either rect of complex */ + runs1 = r1->runs; + if (region_isRect(r2)) { + runs2 = run2_tmp; + runs_set_rect(runs2, &r2->bounds); + } + else { + runs2 = r2->runs; + } + + { + int flags = 0; + + while (runs1[0] != YSENTINEL && runs2[0] != YSENTINEL) + { + int ytop1 = runs1[0]; + int ybot1 = runs1[1]; + int ytop2 = runs2[0]; + int ybot2 = runs2[1]; + + if (ybot1 <= ytop2) + { + /* band1 over band2 */ + flags |= FLAG_REGION_1; + runs1 = runs_next_scanline( runs1 ); + } + else if (ybot2 <= ytop1) + { + /* band2 over band1 */ + flags |= FLAG_REGION_2; + runs2 = runs_next_scanline( runs2 ); + } + else /* band1 and band2 overlap */ + { + Run* span1; + Run* span2; + int ybot; + + if (ytop1 < ytop2) { + flags |= FLAG_REGION_1; + ytop1 = ytop2; + } else if (ytop2 < ytop1) { + flags |= FLAG_REGION_2; + ytop2 = ytop1; + } + + ybot = (ybot1 < ybot2) ? ybot1 : ybot2; + + span1 = runs1 + 2; + span2 = runs2 + 2; + + while (span1[0] != XSENTINEL && span2[0] != XSENTINEL) + { + int xleft1 = span1[0]; + int xright1 = span1[1]; + int xleft2 = span2[0]; + int xright2 = span2[1]; + + RASSERT(xright1 != XSENTINEL); + RASSERT(xright2 != XSENTINEL); + + if (xright1 <= xleft2) { + flags |= FLAG_REGION_1; + span1 += 2; + } + else if (xright2 <= xleft1) { + flags |= FLAG_REGION_2; + span2 += 2; + } + else { + int xright; + + if (xleft1 < xleft2) { + flags |= FLAG_REGION_1; + xleft1 = xleft2; + } else if (xleft2 < xleft1) { + flags |= FLAG_REGION_2; + xleft2 = xleft1; + } + + xright = (xright1 < xright2) ? xright1 : xright2; + + flags |= FLAG_REGION_BOTH; + + if (xright == xright1) + span1 += 2; + if (xright == xright2) + span2 += 2; + } + } + + if (span1[0] != XSENTINEL) { + flags |= FLAG_REGION_1; + } + + if (span2[0] != XSENTINEL) { + flags |= FLAG_REGION_2; + } + + if (ybot == ybot1) + runs1 = runs_next_scanline( runs1 ); + + if (ybot == ybot2) + runs2 = runs_next_scanline( runs2 ); + } + } + + if (runs1[0] != YSENTINEL) { + flags |= FLAG_REGION_1; + } + + if (runs2[0] != YSENTINEL) { + flags |= FLAG_REGION_2; + } + + if ( !(flags & FLAG_REGION_BOTH) ) { + /* no intersection at all */ + return SKIN_OUTSIDE; + } + + if ( (flags & FLAG_REGION_2) != 0 ) { + /* intersection + overlap */ + return SKIN_OVERLAP; + } + + return SKIN_INSIDE; + } +} + +typedef struct { + Run* runs1; + Run* runs2; + Run* runs_base; + Run* runs; + RunStore* store; + Region result[1]; + Run runs1_rect[ RUNS_RECT_COUNT ]; + Run runs2_rect[ RUNS_RECT_COUNT ]; +} RegionOperator; + + +static void +region_operator_init( RegionOperator* o, + Region* r1, + Region* r2 ) +{ + int run1_count, run2_count; + int maxruns; + + RASSERT( !region_isEmpty(r1) ); + RASSERT( !region_isEmpty(r2) ); + + if (region_isRect(r1)) { + run1_count = RUNS_RECT_COUNT; + o->runs1 = o->runs1_rect; + runs_set_rect( o->runs1, &r1->bounds ); + } else { + o->runs1 = r1->runs; + run1_count = runs_get_count(r1->runs); + } + + if (region_isRect(r2)) { + run2_count = RUNS_RECT_COUNT; + o->runs2 = o->runs2_rect; + runs_set_rect( o->runs2, &r2->bounds ); + } else { + o->runs2 = r2->runs; + run2_count = runs_get_count(r2->runs); + } + + maxruns = run1_count < run2_count ? run2_count : run1_count; + o->store = runstore_alloc( 3*maxruns ); + o->runs_base = runstore_to_runs(o->store); +} + + +static void +region_operator_do( RegionOperator* o, int wanted ) +{ + Run* runs1 = o->runs1; + Run* runs2 = o->runs2; + Run* runs = o->runs_base; + int ytop1 = runs1[0]; + int ytop2 = runs2[0]; + + if (ytop1 != YSENTINEL && ytop2 != YSENTINEL) + { + int ybot1, ybot2; + + while (ytop1 != YSENTINEL && ytop2 != YSENTINEL) + { + int ybot; + + ybot1 = runs1[1]; + ybot2 = runs2[1]; + + RASSERT(ybot1 != YSENTINEL); + RASSERT(ybot2 != YSENTINEL); + + if (ybot1 <= ytop2) { + if (wanted & FLAG_REGION_1) + runs = runs_copy_scanline_adj( runs, runs1, ytop1, ybot1 ); + runs1 = runs_next_scanline( runs1 ); + ytop1 = runs1[0]; + continue; + } + + if (ybot2 <= ytop1) { + if (wanted & FLAG_REGION_2) + runs = runs_copy_scanline_adj( runs, runs2, ytop2, ybot2 ); + runs2 = runs_next_scanline(runs2); + ytop2 = runs2[0]; + continue; + } + + if (ytop1 < ytop2) { + if (wanted & FLAG_REGION_1) + runs = runs_copy_scanline_adj( runs, runs1, ytop1, ytop2 ); + ytop1 = ytop2; + } + else if (ytop2 < ytop1) { + if (wanted & FLAG_REGION_2) + runs = runs_copy_scanline_adj( runs, runs2, ytop2, ytop1 ); + ytop2 = ytop1; + } + + ybot = (ybot1 <= ybot2) ? ybot1 : ybot2; + + runs[0] = (Run) ytop1; + runs[1] = (Run) ybot; + + /* do the common band */ + { + Run* span1 = runs1 + 2; + Run* span2 = runs2 + 2; + Run* span = runs + 2; + int xleft1 = span1[0]; + int xleft2 = span2[0]; + int xright1, xright2; + + while (xleft1 != XSENTINEL && xleft2 != XSENTINEL) + { + int xright; + + xright1 = span1[1]; + xright2 = span2[1]; + + RASSERT(xright1 != XSENTINEL); + RASSERT(xright2 != XSENTINEL); + + if (xright1 <= xleft2) { + if (wanted & FLAG_REGION_1) + span = runs_add_span( span, xleft1, xright1 ); + span1 += 2; + xleft1 = span1[0]; + continue; + } + + if (xright2 <= xleft1) { + if (wanted & FLAG_REGION_2) + span = runs_add_span( span, xleft2, xright2 ); + span2 += 2; + xleft2 = span2[0]; + continue; + } + + if (xleft1 < xleft2) { + if (wanted & FLAG_REGION_1) + span = runs_add_span( span, xleft1, xleft2 ); + xleft1 = xleft2; + } + + else if (xleft2 < xleft1) { + if (wanted & FLAG_REGION_2) + span = runs_add_span( span, xleft2, xleft1 ); + xleft2 = xleft1; + } + + xright = (xright1 <= xright2) ? xright1 : xright2; + + if (wanted & FLAG_REGION_BOTH) + span = runs_add_span( span, xleft1, xright ); + + xleft1 = xleft2 = xright; + + if (xright == xright1) { + span1 += 2; + xleft1 = span1[0]; + } + if (xright == xright2) { + span2 += 2; + xleft2 = span2[0]; + } + } + + if (wanted & FLAG_REGION_1) { + while (xleft1 != XSENTINEL) { + RASSERT(span1[1] != XSENTINEL); + span[0] = (Run) xleft1; + span[1] = span1[1]; + span += 2; + span1 += 2; + xleft1 = span1[0]; + } + } + + if (wanted & FLAG_REGION_2) { + while (xleft2 != XSENTINEL) { + RASSERT(span2[1] != XSENTINEL); + span[0] = (Run) xleft2; + span[1] = span2[1]; + span += 2; + span2 += 2; + xleft2 = span2[0]; + } + } + + if (span > runs + 2) { + span[0] = XSENTINEL; + runs = span + 1; + } + } + + ytop1 = ytop2 = ybot; + + if (ybot == ybot1) { + runs1 = runs_next_scanline( runs1 ); + ytop1 = runs1[0]; + } + if (ybot == ybot2) { + runs2 = runs_next_scanline( runs2 ); + ytop2 = runs2[0]; + } + } + } + + if ((wanted & FLAG_REGION_1) != 0) { + while (ytop1 != YSENTINEL) { + runs = runs_copy_scanline_adj( runs, runs1, ytop1, runs1[1] ); + runs1 = runs_next_scanline(runs1); + ytop1 = runs1[0]; + } + } + + if ((wanted & FLAG_REGION_2) != 0) { + while (ytop2 != YSENTINEL) { + runs = runs_copy_scanline_adj( runs, runs2, ytop2, runs2[1] ); + runs2 = runs_next_scanline(runs2); + ytop2 = runs2[0]; + } + } + + runs[0] = YSENTINEL; + o->runs = runs + 1; +} + +/* returns 1 if the result is not empty */ +static int +region_operator_done( RegionOperator* o ) +{ + Run* src = o->runs; + int count; + SkinBox minmax; + RunStore* store; + + if (src <= o->runs_base + 1) { + /* result is empty */ + skin_region_init_empty( o->result ); + return 0; + } + + /* coalesce the temp runs in-place and compute the corresponding bounds */ + minmax.x1 = minmax.y1 = INT_MAX; + minmax.x2 = minmax.y2 = INT_MIN; + + count = runs_coalesce( o->runs_base, o->runs_base, &minmax ); + if (count == 1) { + /* result is empty */ + skin_region_init_empty( o->result ); + } + else + { + skin_box_to_rect( &minmax, &o->result->bounds ); + if (count == RUNS_RECT_COUNT) { + o->result->runs = RUNS_RECT; + } + else + { + /* result is complex */ + store = runstore_alloc( count ); + o->result->runs = runstore_to_runs(store); + memcpy( o->result->runs, o->runs_base, count*sizeof(Run) ); + } + } + + /* release temporary runstore */ + runstore_unrefp( &o->store ); + + return region_isEmpty(o->result); +} + + + +int +skin_region_intersect( SkinRegion* r, SkinRegion* r2 ) +{ + RegionOperator oper[1]; + + if (region_isEmpty(r)) + return 0; + + if (region_isEmpty(r2)) + return 1; + + if ( skin_rect_contains_rect( &r->bounds, &r2->bounds ) == SKIN_OUTSIDE ) { + skin_region_init_empty(r); + return 0; + } + + region_operator_init( oper, r, r2 ); + region_operator_do( oper, FLAG_REGION_BOTH ); + region_operator_done( oper ); + + skin_region_swap( r, oper->result ); + skin_region_reset( oper->result ); + + return region_isEmpty( r ); +} + + +/* performs r = (intersect r (region+_from_rect rect)), returns true iff + the resulting region is not empty */ +int +skin_region_intersect_rect( SkinRegion* r, SkinRect* rect ) +{ + Region r2[1]; + + skin_region_init_rect( r2, rect ); + return skin_region_intersect( r, r2 ); +} + +/* performs r = (union r r2) */ +void +skin_region_union( SkinRegion* r, SkinRegion* r2 ) +{ + RegionOperator oper[1]; + + if (region_isEmpty(r)) { + skin_region_copy(r, r2); + return; + } + + if (region_isEmpty(r2)) + return; + + region_operator_init( oper, r, r2 ); + region_operator_do( oper, FLAG_REGION_1|FLAG_REGION_2|FLAG_REGION_BOTH ); + region_operator_done( oper ); + + skin_region_swap( r, oper->result ); + skin_region_reset( oper->result ); +} + +void +skin_region_union_rect( SkinRegion* r, SkinRect* rect ) +{ + Region r2[1]; + + skin_region_init_rect(r2, rect); + return skin_region_union( r, r2 ); +} + +/* performs r = (difference r r2) */ +void +skin_region_substract( SkinRegion* r, SkinRegion* r2 ) +{ + RegionOperator oper[1]; + + if (region_isEmpty(r) || region_isEmpty(r2)) + return; + + if ( skin_rect_contains_rect( &r->bounds, &r2->bounds ) == SKIN_OUTSIDE ) { + skin_region_init_empty(r); + return; + } + + region_operator_init( oper, r, r2 ); + region_operator_do( oper, FLAG_REGION_1 ); + region_operator_done( oper ); + + skin_region_swap( r, oper->result ); + skin_region_reset( oper->result ); +} + +void +skin_region_substract_rect( SkinRegion* r, SkinRect* rect ) +{ + Region r2[1]; + + skin_region_init_rect(r2, rect); + return skin_region_substract( r, r2 ); +} + +/* performs r = (xor r r2) */ +void +skin_region_xor( SkinRegion* r, SkinRegion* r2 ) +{ + RegionOperator oper[1]; + + if (region_isEmpty(r)) { + skin_region_copy(r, r2); + return; + } + + if (region_isEmpty(r2)) + return; + + if ( skin_rect_contains_rect( &r->bounds, &r2->bounds ) == SKIN_OUTSIDE ) { + skin_region_init_empty(r); + return; + } + + region_operator_init( oper, r, r2 ); + region_operator_do( oper, FLAG_REGION_1 ); + region_operator_done( oper ); + + skin_region_swap( r, oper->result ); + skin_region_reset( oper->result ); +} + + +void +skin_region_iterator_init( SkinRegionIterator* iter, + SkinRegion* region ) +{ + iter->region = region; + iter->band = NULL; + iter->span = NULL; +} + +int +skin_region_iterator_next( SkinRegionIterator* iter, SkinRect *rect ) +{ + static const Run dummy[ 2 ] = { XSENTINEL, YSENTINEL }; + + Run* span = iter->span; + Run* band = iter->band; + + if (span == NULL) { + Region* r = iter->region; + if (region_isEmpty(r)) + return 0; + if (region_isRect(r)) { + rect[0] = r->bounds; + iter->span = (Run*) dummy; + return 1; + } + iter->band = band = r->runs; + iter->span = span = r->runs + 2; + } + else if (band == NULL) + return 0; + + while (span[0] == XSENTINEL) { + band = span + 1; + if (band[0] == YSENTINEL || band[1] == YSENTINEL) + return 0; + + iter->band = band; + iter->span = span = band + 2; + } + + if (span[1] == XSENTINEL) + return 0; + + rect->pos.y = band[0]; + rect->pos.x = span[0]; + rect->size.h = band[1] - band[0]; + rect->size.w = span[1] - span[0]; + + iter->span = span + 2; + return 1; +} + +int +skin_region_iterator_next_box( SkinRegionIterator* iter, SkinBox *box ) +{ + SkinRect rect; + int result = skin_region_iterator_next( iter, &rect ); + + if (result) + skin_box_from_rect( box, &rect ); + + return result; +} + +#ifdef UNIT_TEST + +#include <stdio.h> +#include <stdlib.h> +#include "skin_rect.c" + +static void +panic(void) +{ + *((char*)0) = 1; + exit(0); +} + +static void +_expectCompare( Region* r, const SkinBox* boxes, int count ) +{ + if (count == 0) { + if ( !skin_region_is_empty(r) ) { + printf( " result is not empty\n" ); + panic(); + } + } + else if (count == 1) { + SkinRect rect1, rect2; + if ( !skin_region_is_rect(r) ) { + printf( " result is not a rectangle\n" ); + panic(); + } + skin_region_get_bounds( r, &rect1 ); + skin_box_to_rect( (SkinBox*)boxes, &rect2 ); + if ( !skin_rect_equals( &rect1, &rect2 ) ) { + printf( " result is (%d,%d,%d,%d), expected (%d,%d,%d,%d)\n", + rect1.pos.x, rect1.pos.y, + rect1.pos.x + rect1.size.w, rect1.pos.y + rect1.size.h, + rect2.pos.x, rect2.pos.y, + rect2.pos.x + rect2.size.w, rect2.pos.y + rect2.size.h ); + panic(); + } + } + else { + SkinRegionIterator iter; + SkinBox b; + int n; + + skin_region_iterator_init( &iter, r ); + n = 0; + while (n < count) { + if ( !skin_region_iterator_next_box( &iter, &b ) ) { + printf( "missing region box (%d, %d, %d, %d)\n", + boxes->x1, boxes->y1, boxes->x2, boxes->y2 ); + panic(); + } + + if (b.x1 != boxes->x1 || b.x2 != boxes->x2|| + b.y1 != boxes->y1 || b.y2 != boxes->y2) + { + printf( "invalid region box (%d,%d,%d,%d) expecting (%d,%d,%d,%d)\n", + b.x1, b.y1, b.x2, b.y2, + boxes->x1, boxes->y1, boxes->x2, boxes->y2 ); + panic(); + } + boxes += 1; + n += 1; + } + + if ( skin_region_iterator_next_box( &iter, &b ) ) { + printf( "excess region box (%d,%d,%d,%d)\n", + b.x1, b.y1, b.x2, b.y2 ); + panic(); + } + } +} + + +static void +expectEmptyRegion( Region* r ) +{ + printf( "expectEmptyRegion: " ); + if (!skin_region_is_empty(r)) { + printf( "region not empty !!\n" ); + panic(); + } + printf( "ok\n" ); +} + +static void +expectTestIntersect( Region* r1, Region* r2, SkinOverlap overlap ) +{ + SkinOverlap result; + printf( "expectTestIntersect(%d): ", overlap ); + result = skin_region_test_intersect(r1, r2); + if (result != overlap) { + printf( "bad result %d, expected %d\n", result, overlap ); + panic(); + } + printf( "ok\n" ); +} + +static void +expectRectRegion( Region* r, int x1, int y1, int x2, int y2 ) +{ + SkinRect rect; + SkinBox b; + + printf( "expectRectRegion(%d,%d,%d,%d): ",x1,y1,x2,y2 ); + if (!skin_region_is_rect(r)) { + printf( "region not rect !!\n" ); + panic(); + } + + skin_region_get_bounds( r, &rect ); + skin_box_from_rect( &b, &rect ); + + if (b.x1 != x1 || b.x2 != x2 || b.y1 != y1 || b.y2 != y2) { + printf( "rect region bounds are (%d,%d,%d,%d), expecting (%d,%d,%d,%d)\n", + b.x1, b.y1, b.x2, b.y2, x1, y1, x2, y2 ); + panic(); + } + printf( "ok\n" ); +} + +static void +expectComplexRegion( Region* r, const SkinBox* boxes, int count ) +{ + SkinRegionIterator iter; + SkinBox b; + int n; + + printf( "expectComplexRegion(): " ); + if (!skin_region_is_complex(r)) { + printf( "region is not complex !!\n" ); + panic(); + } + _expectCompare( r, boxes, count ); + printf( "ok\n" ); +} + +static void +expectIntersect( Region* r1, Region* r2, const SkinBox* boxes, int count ) +{ + SkinRegion r[1]; + + printf( "expectIntersect(%d): ", count ); + skin_region_init_copy( r, r1 ); + skin_region_intersect( r, r2 ); + _expectCompare( r, boxes, count ); + printf( "ok\n" ); +} + +static void +expectUnion( Region* r1, Region* r2, const SkinBox* boxes, int count ) +{ + SkinRegion r[1]; + + printf( "expectUnion(%d): ", count ); + skin_region_init_copy( r, r1 ); + skin_region_union( r, r2 ); + _expectCompare( r, boxes, count ); + printf( "ok\n" ); +} + + +static void +expectSubstract( Region* r1, Region* r2, const SkinBox* boxes, int count ) +{ + SkinRegion r[1]; + + printf( "expectSubstract(%d): ", count ); + skin_region_init_copy( r, r1 ); + skin_region_substract( r, r2 ); + _expectCompare( r, boxes, count ); + printf( "ok\n" ); +} + + +int main( void ) +{ + SkinRegion r[1], r2[1]; + + skin_region_init_empty( r ); + expectEmptyRegion( r ); + + skin_region_init( r, 10, 20, 110, 120 ); + expectRectRegion( r, 10, 20, 110, 120 ); + + skin_region_translate( r, 50, 80 ); + expectRectRegion( r, 60, 100, 160, 200 ); + + skin_region_init( r, 10, 10, 40, 40 ); + skin_region_init( r2, 20, 20, 50, 50 ); + expectTestIntersect( r, r2, SKIN_OVERLAP ); + + skin_region_translate(r2, +20, + 20 ); + expectTestIntersect( r, r2, SKIN_OUTSIDE ); + + skin_region_translate(r2, -30, -30 ); + expectTestIntersect( r, r2, SKIN_INSIDE ); + + { + static const SkinBox result1[1] = { + { 20, 20, 40, 40 } + }; + static const SkinBox result2[3] = { + { 10, 10, 40, 20 }, + { 10, 20, 50, 40 }, + { 20, 40, 50, 50 }, + }; + static const SkinBox result3[2] = { + { 10, 10, 40, 20 }, + { 10, 20, 20, 40 }, + }; + + skin_region_init( r, 10, 10, 40, 40 ); + skin_region_init( r2, 20, 20, 50, 50 ); + expectIntersect( r, r2, result1, 1 ); + expectUnion( r, r2, result2, 3 ); + expectSubstract( r, r2, result3, 2 ); + } + + return 0; +} + +#endif /* UNIT_TEST */ |