| /* 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 */ |