Mercurial > pygame
view src/bitmask.c @ 3172:d1b367fc08bc tests-returncode
Use a return code of 1 when the tests fail.
| author | Thomas Kluyver <takowl@gmail.com> |
|---|---|
| date | Wed, 09 Jan 2013 10:13:57 +0000 |
| parents | 5320c3356ae9 |
| children |
line wrap: on
line source
/* Bitmask Collision Detection Library 1.5 Copyright (C) 2002-2005 Ulf Ekstrom except for the bitcount function which is copyright (C) Donald W. Gillies, 1992, and the other bitcount function which was taken from Jorg Arndt's excellent "Algorithms for Programmers" text. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <stdlib.h> #include <stddef.h> #include <string.h> #include "bitmask.h" #ifndef INLINE #warning No INLINE definition in bitmask.h, performance may suffer. #endif #define MIN(a,b) ((a) <= (b) ? (a) : (b)) #define MAX(a,b) ((a) >= (b) ? (a) : (b)) /* The code by Gillies is slightly (1-3%) faster than the more readable code below */ #define GILLIES static INLINE unsigned int bitcount(BITMASK_W n) { if (BITMASK_W_LEN == 32) { #ifdef GILLIES /* (C) Donald W. Gillies, 1992. All rights reserved. You may reuse this bitcount() function anywhere you please as long as you retain this Copyright Notice. */ register unsigned long tmp; return (tmp = (n) - (((n) >> 1) & 033333333333) - (((n) >> 2) & 011111111111), tmp = ((tmp + (tmp >> 3)) & 030707070707), tmp = (tmp + (tmp >> 6)), tmp = (tmp + (tmp >> 12) + (tmp >> 24)) & 077); /* End of Donald W. Gillies bitcount code */ #else /* This piece taken from Jorg Arndt's "Algorithms for Programmers" */ n = ((n>>1) & 0x55555555) + (n & 0x55555555); // 0-2 in 2 bits n = ((n>>2) & 0x33333333) + (n & 0x33333333); // 0-4 in 4 bits n = ((n>>4) + n) & 0x0f0f0f0f; // 0-8 in 4 bits n += n>> 8; // 0-16 in 8 bits n += n>>16; // 0-32 in 8 bits return n & 0xff; #endif } else if (BITMASK_W_LEN == 64) { n = ((n>>1) & 0x5555555555555555) + (n & 0x5555555555555555); n = ((n>>2) & 0x3333333333333333) + (n & 0x3333333333333333); n = ((n>>4) + n) & 0x0f0f0f0f0f0f0f0f; n += n>> 8; n += n>>16; n += n>>32; return n & 0xff; } else { /* Handle non-32 or 64 bit case the slow way */ unsigned int nbits = 0; while (n) { if (n & 1) nbits++; n = n >> 1; } return nbits; } } bitmask_t *bitmask_create(int w, int h) { bitmask_t *temp; temp = malloc(offsetof(bitmask_t,bits) + h*((w - 1)/BITMASK_W_LEN + 1)*sizeof(BITMASK_W)); if (!temp) return 0; temp->w = w; temp->h = h; bitmask_clear(temp); return temp; } void bitmask_free(bitmask_t *m) { free(m); } void bitmask_clear(bitmask_t *m) { memset(m->bits,0,m->h*((m->w - 1)/BITMASK_W_LEN + 1)*sizeof(BITMASK_W)); } void bitmask_fill(bitmask_t *m) { int len, shift; BITMASK_W *pixels, cmask, full; len = m->h*((m->w - 1)/BITMASK_W_LEN); shift = BITMASK_W_LEN - (m->w % BITMASK_W_LEN); full = ~(BITMASK_W)0; cmask = (~(BITMASK_W)0) >> shift; /* fill all the pixels that aren't in the rightmost BITMASK_Ws */ for (pixels = m->bits; pixels < (m->bits + len); pixels++) { *pixels = full; } /* for the rightmost BITMASK_Ws, use cmask to ensure we aren't setting bits that are outside of the mask */ for (pixels = m->bits + len; pixels < (m->bits + len + m->h); pixels++) { *pixels = cmask; } } void bitmask_invert(bitmask_t *m) { int len, shift; BITMASK_W *pixels, cmask; len = m->h*((m->w - 1)/BITMASK_W_LEN); shift = BITMASK_W_LEN - (m->w % BITMASK_W_LEN); cmask = (~(BITMASK_W)0) >> shift; /* flip all the pixels that aren't in the rightmost BITMASK_Ws */ for (pixels = m->bits; pixels < (m->bits + len); pixels++) { *pixels = ~(*pixels); } /* for the rightmost BITMASK_Ws, & with cmask to ensure we aren't setting bits that are outside of the mask */ for (pixels = m->bits + len; pixels < (m->bits + len + m->h); pixels++) { *pixels = cmask & ~(*pixels); } } unsigned int bitmask_count(bitmask_t *m) { BITMASK_W *pixels; unsigned int tot = 0; for (pixels=m->bits; pixels<(m->bits+m->h*((m->w-1)/BITMASK_W_LEN + 1)); pixels++) { tot += bitcount(*pixels); } return tot; } int bitmask_overlap(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset) { const BITMASK_W *a_entry,*a_end; const BITMASK_W *b_entry; const BITMASK_W *ap,*app,*bp; unsigned int shift,rshift,i,astripes,bstripes; if ((xoffset >= a->w) || (yoffset >= a->h) || (b->h + yoffset <= 0) || (b->w + xoffset <= 0)) return 0; if (xoffset >= 0) { swapentry: if (yoffset >= 0) { a_entry = a->bits + a->h*((unsigned int)xoffset/BITMASK_W_LEN) + yoffset; a_end = a_entry + MIN(b->h,a->h - yoffset); b_entry = b->bits; } else { a_entry = a->bits + a->h*((unsigned int)xoffset/BITMASK_W_LEN); a_end = a_entry + MIN(b->h + yoffset,a->h); b_entry = b->bits - yoffset; } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = ((unsigned int)(a->w - 1))/BITMASK_W_LEN - (unsigned int)xoffset/BITMASK_W_LEN; bstripes = ((unsigned int)(b->w - 1))/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (ap = a_entry, app = ap + a->h, bp = b_entry;ap < a_end;) if ((*ap++ >> shift) & *bp || (*app++ << rshift) & *bp++) return 1; a_entry += a->h; a_end += a->h; b_entry += b->h; } for (ap = a_entry,bp = b_entry;ap < a_end;) if ((*ap++ >> shift) & *bp++) return 1; return 0; } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (ap = a_entry, app = ap + a->h, bp = b_entry;ap < a_end;) if ((*ap++ >> shift) & *bp || (*app++ << rshift) & *bp++) return 1; a_entry += a->h; a_end += a->h; b_entry += b->h; } return 0; } } else /* xoffset is a multiple of the stripe width, and the above routines wont work */ { astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;) if (*ap++ & *bp++) return 1; a_entry += a->h; a_end += a->h; b_entry += b->h; } return 0; } } else { const bitmask_t *c = a; a = b; b = c; xoffset *= -1; yoffset *= -1; goto swapentry; } } /* Will hang if there are no bits set in w! */ static INLINE int firstsetbit(BITMASK_W w) { int i = 0; while ((w & 1) == 0) { i++; w/=2; } return i; } /* x and y are given in the coordinates of mask a, and are untouched if there is no overlap */ int bitmask_overlap_pos(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset, int *x, int *y) { const BITMASK_W *a_entry,*a_end, *b_entry, *ap, *bp; unsigned int shift,rshift,i,astripes,bstripes,xbase; if ((xoffset >= a->w) || (yoffset >= a->h) || (yoffset <= - b->h)) return 0; if (xoffset >= 0) { xbase = xoffset/BITMASK_W_LEN; /* first stripe from mask a */ if (yoffset >= 0) { a_entry = a->bits + a->h*xbase + yoffset; a_end = a_entry + MIN(b->h,a->h - yoffset); b_entry = b->bits; } else { a_entry = a->bits + a->h*xbase; a_end = a_entry + MIN(b->h + yoffset,a->h); b_entry = b->bits - yoffset; yoffset = 0; /* relied on below */ } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; bstripes = (b->w - 1)/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) if (*ap & (*bp << shift)) { *y = ap - a_entry + yoffset; *x = (xbase + i)*BITMASK_W_LEN + firstsetbit(*ap & (*bp << shift)); return 1; } a_entry += a->h; a_end += a->h; for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) if (*ap & (*bp >> rshift)) { *y = ap - a_entry + yoffset; *x = (xbase + i + 1)*BITMASK_W_LEN + firstsetbit(*ap & (*bp >> rshift)); return 1; } b_entry += b->h; } for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) if (*ap & (*bp << shift)) { *y = ap - a_entry + yoffset; *x = (xbase + astripes)*BITMASK_W_LEN + firstsetbit(*ap & (*bp << shift)); return 1; } return 0; } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) if (*ap & (*bp << shift)) { *y = ap - a_entry + yoffset; *x = (xbase + i)*BITMASK_W_LEN + firstsetbit(*ap & (*bp << shift)); return 1; } a_entry += a->h; a_end += a->h; for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) if (*ap & (*bp >> rshift)) { *y = ap - a_entry + yoffset; *x = (xbase + i + 1)*BITMASK_W_LEN + firstsetbit(*ap & (*bp >> rshift)); return 1; } b_entry += b->h; } return 0; } } else /* xoffset is a multiple of the stripe width, and the above routines won't work. This way is also slightly faster. */ { astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) { if (*ap & *bp) { *y = ap - a_entry + yoffset; *x = (xbase + i)*BITMASK_W_LEN + firstsetbit(*ap & *bp); return 1; } } a_entry += a->h; a_end += a->h; b_entry += b->h; } return 0; } } else { if (bitmask_overlap_pos(b,a,-xoffset,-yoffset,x,y)) { *x += xoffset; *y += yoffset; return 1; } else return 0; } } int bitmask_overlap_area(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset) { const BITMASK_W *a_entry,*a_end, *b_entry, *ap,*bp; unsigned int shift,rshift,i,astripes,bstripes; unsigned int count = 0; if ((xoffset >= a->w) || (yoffset >= a->h) || (b->h + yoffset <= 0) || (b->w + xoffset <= 0)) return 0; if (xoffset >= 0) { swapentry: if (yoffset >= 0) { a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN) + yoffset; a_end = a_entry + MIN(b->h,a->h - yoffset); b_entry = b->bits; } else { a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN); a_end = a_entry + MIN(b->h + yoffset,a->h); b_entry = b->bits - yoffset; } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; bstripes = (b->w - 1)/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) count += bitcount(((*ap >> shift) | (*(ap + a->h) << rshift)) & *bp); a_entry += a->h; a_end += a->h; b_entry += b->h; } for (ap = a_entry,bp = b_entry;ap < a_end;) count += bitcount((*ap++ >> shift) & *bp++); return count; } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) count += bitcount(((*ap >> shift) | (*(ap + a->h) << rshift)) & *bp); a_entry += a->h; a_end += a->h; b_entry += b->h; } return count; } } else /* xoffset is a multiple of the stripe width, and the above routines wont work */ { astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;) count += bitcount(*ap++ & *bp++); a_entry += a->h; a_end += a->h; b_entry += b->h; } return count; } } else { const bitmask_t *c = a; a = b; b = c; xoffset *= -1; yoffset *= -1; goto swapentry; } } /* Makes a mask of the overlap of two other masks */ void bitmask_overlap_mask(const bitmask_t *a, const bitmask_t *b, bitmask_t *c, int xoffset, int yoffset) { const BITMASK_W *a_entry,*a_end, *ap; const BITMASK_W *b_entry, *b_end, *bp; BITMASK_W *c_entry, *c_end, *cp; int shift,rshift,i,astripes,bstripes; if ((xoffset >= a->w) || (yoffset >= a->h) || (yoffset <= - b->h)) return; if (xoffset >= 0) { if (yoffset >= 0) { a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN) + yoffset; c_entry = c->bits + c->h*(xoffset/BITMASK_W_LEN) + yoffset; a_end = a_entry + MIN(b->h,a->h - yoffset); b_entry = b->bits; } else { a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN); c_entry = c->bits + c->h*(xoffset/BITMASK_W_LEN); a_end = a_entry + MIN(b->h + yoffset,a->h); b_entry = b->bits - yoffset; } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; bstripes = (b->w - 1)/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) *cp = *ap & (*bp << shift); a_entry += a->h; c_entry += c->h; a_end += a->h; for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) *cp = *ap & (*bp >> rshift); b_entry += b->h; } for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) *cp = *ap & (*bp << shift); } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) *cp = *ap & (*bp << shift); a_entry += a->h; c_entry += c->h; a_end += a->h; for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) *cp = *ap & (*bp >> rshift); b_entry += b->h; } } } else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ { astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry,cp = c_entry;ap < a_end;ap++,bp++,cp++) { *cp = *ap & *bp; } a_entry += a->h; c_entry += c->h; a_end += a->h; b_entry += b->h; } } } else { xoffset *= -1; yoffset *= -1; if (yoffset >= 0) { b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN) + yoffset; b_end = b_entry + MIN(a->h,b->h - yoffset); a_entry = a->bits; c_entry = c->bits; } else { b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN); b_end = b_entry + MIN(a->h + yoffset,b->h); a_entry = a->bits - yoffset; c_entry = c->bits - yoffset; } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = (b->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; bstripes = (a->w - 1)/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) *cp = *ap & (*bp >> shift); b_entry += b->h; b_end += b->h; for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) *cp = *ap & (*bp <<rshift); a_entry += a->h; c_entry += c->h; } for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) *cp = *ap & (*bp >> shift); } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) *cp = *ap & (*bp >> shift); b_entry += b->h; b_end += b->h; for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) *cp = *ap & (*bp << rshift); a_entry += a->h; c_entry += c->h; } } } else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ { astripes = (MIN(a->w,b->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (bp = b_entry,ap = a_entry,cp = c_entry;bp < b_end;bp++,ap++,cp++) { *cp = *ap & *bp; } b_entry += b->h; b_end += b->h; a_entry += a->h; c_entry += c->h; } } xoffset *= -1; yoffset *= -1; } /* Zero out bits outside the mask rectangle (to the right), if there is a chance we were drawing there. */ if (xoffset + b->w > c->w) { BITMASK_W edgemask; int n = c->w/BITMASK_W_LEN; shift = (n + 1)*BITMASK_W_LEN - c->w; edgemask = (~(BITMASK_W)0) >> shift; c_end = c->bits + n*c->h + MIN(c->h,b->h + yoffset); for (cp = c->bits + n*c->h + MAX(yoffset,0);cp<c_end;cp++) *cp &= edgemask; } } /* Draws mask b onto mask a (bitwise OR) */ void bitmask_draw(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset) { BITMASK_W *a_entry,*a_end, *ap; const BITMASK_W *b_entry, *b_end, *bp; int shift,rshift,i,astripes,bstripes; if ((xoffset >= a->w) || (yoffset >= a->h) || (yoffset <= - b->h)) return; if (xoffset >= 0) { if (yoffset >= 0) { a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN) + yoffset; a_end = a_entry + MIN(b->h,a->h - yoffset); b_entry = b->bits; } else { a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN); a_end = a_entry + MIN(b->h + yoffset,a->h); b_entry = b->bits - yoffset; } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; bstripes = (b->w - 1)/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap |= (*bp << shift); a_entry += a->h; a_end += a->h; for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap |= (*bp >> rshift); b_entry += b->h; } for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap |= (*bp << shift); } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap |= (*bp << shift); a_entry += a->h; a_end += a->h; for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap |= (*bp >> rshift); b_entry += b->h; } } } else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ { astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) { *ap |= *bp; } a_entry += a->h; a_end += a->h; b_entry += b->h; } } } else { xoffset *= -1; yoffset *= -1; if (yoffset >= 0) { b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN) + yoffset; b_end = b_entry + MIN(a->h,b->h - yoffset); a_entry = a->bits; } else { b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN); b_end = b_entry + MIN(a->h + yoffset,b->h); a_entry = a->bits - yoffset; } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = (b->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; bstripes = (a->w - 1)/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap |= (*bp >> shift); b_entry += b->h; b_end += b->h; for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap |= (*bp <<rshift); a_entry += a->h; } for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap |= (*bp >> shift); } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap |= (*bp >> shift); b_entry += b->h; b_end += b->h; for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap |= (*bp << rshift); a_entry += a->h; } } } else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ { astripes = (MIN(a->w,b->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) { *ap |= *bp; } b_entry += b->h; b_end += b->h; a_entry += a->h; } } xoffset *= -1; yoffset *= -1; } /* Zero out bits outside the mask rectangle (to the right), if there is a chance we were drawing there. */ if (xoffset + b->w > a->w) { BITMASK_W edgemask; int n = a->w/BITMASK_W_LEN; shift = (n + 1)*BITMASK_W_LEN - a->w; edgemask = (~(BITMASK_W)0) >> shift; a_end = a->bits + n*a->h + MIN(a->h,b->h + yoffset); for (ap = a->bits + n*a->h + MAX(yoffset,0);ap<a_end;ap++) *ap &= edgemask; } } /* Erases mask b from mask a (a &= ~b) */ void bitmask_erase(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset) { BITMASK_W *a_entry,*a_end, *ap; const BITMASK_W *b_entry, *b_end, *bp; int shift,rshift,i,astripes,bstripes; if ((xoffset >= a->w) || (yoffset >= a->h) || (yoffset <= - b->h)) return; if (xoffset >= 0) { if (yoffset >= 0) { a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN) + yoffset; a_end = a_entry + MIN(b->h,a->h - yoffset); b_entry = b->bits; } else { a_entry = a->bits + a->h*(xoffset/BITMASK_W_LEN); a_end = a_entry + MIN(b->h + yoffset,a->h); b_entry = b->bits - yoffset; } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = (a->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; bstripes = (b->w - 1)/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap &= ~(*bp << shift); a_entry += a->h; a_end += a->h; for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap &= ~(*bp >> rshift); b_entry += b->h; } for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap &= ~(*bp << shift); } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap &= ~(*bp << shift); a_entry += a->h; a_end += a->h; for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) *ap &= ~(*bp >> rshift); b_entry += b->h; } } } else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ { astripes = (MIN(b->w,a->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (ap = a_entry,bp = b_entry;ap < a_end;ap++,bp++) { *ap &= ~*bp; } a_entry += a->h; a_end += a->h; b_entry += b->h; } } } else { xoffset *= -1; yoffset *= -1; if (yoffset >= 0) { b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN) + yoffset; b_end = b_entry + MIN(a->h,b->h - yoffset); a_entry = a->bits; } else { b_entry = b->bits + b->h*(xoffset/BITMASK_W_LEN); b_end = b_entry + MIN(a->h + yoffset,b->h); a_entry = a->bits - yoffset; } shift = xoffset & BITMASK_W_MASK; if (shift) { rshift = BITMASK_W_LEN - shift; astripes = (b->w - 1)/BITMASK_W_LEN - xoffset/BITMASK_W_LEN; bstripes = (a->w - 1)/BITMASK_W_LEN + 1; if (bstripes > astripes) /* zig-zag .. zig*/ { for (i=0;i<astripes;i++) { for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap &= ~(*bp >> shift); b_entry += b->h; b_end += b->h; for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap &= ~(*bp <<rshift); a_entry += a->h; } for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap |= (*bp >> shift); } else /* zig-zag */ { for (i=0;i<bstripes;i++) { for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap &= ~(*bp >> shift); b_entry += b->h; b_end += b->h; for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap &= ~(*bp << rshift); a_entry += a->h; } } } else /* xoffset is a multiple of the stripe width, and the above routines won't work. */ { astripes = (MIN(a->w,b->w - xoffset) - 1)/BITMASK_W_LEN + 1; for (i=0;i<astripes;i++) { for (bp = b_entry,ap = a_entry;bp < b_end;bp++,ap++) *ap &= ~*bp; b_entry += b->h; b_end += b->h; a_entry += a->h; } } } } bitmask_t *bitmask_scale(const bitmask_t *m, int w, int h) { bitmask_t *nm; int x,y,nx,ny,dx,dy,dnx,dny; if (w < 1 || h < 1) { nm = bitmask_create(1,1); return nm; } nm = bitmask_create(w,h); if (!nm) return NULL; ny = dny = 0; for (y=0,dy=h; y<m->h; y++,dy+=h) { while (dny < dy) { nx = dnx = 0; for (x=0,dx=w; x < m->w; x++, dx+=w) { if (bitmask_getbit(m,x,y)) { while (dnx < dx) { bitmask_setbit(nm,nx,ny); nx++; dnx += m->w; } } else { while (dnx < dx) { nx++; dnx += m->w; } } } ny++; dny+=m->h; } } return nm; } void bitmask_convolve(const bitmask_t *a, const bitmask_t *b, bitmask_t *o, int xoffset, int yoffset) { int x, y; xoffset += b->w - 1; yoffset += b->h - 1; for (y = 0; y < b->h; y++) for (x = 0; x < b->w; x++) if (bitmask_getbit(b, x, y)) bitmask_draw(o, a, xoffset - x, yoffset - y); }
