Imported contents: kraptor_final_apr_03_2004.tar.gz
[kraptor.git] / src / pmask.c
blob581705770cab6b0663c7b7a17f8011e02a8f5939
1 #include <stdlib.h>
2 #include <string.h>
3 #include "pmask.h"
5 #ifdef USE_ALLEGRO
6 #include <allegro.h>
7 #endif
9 #ifdef main
10 #undef main
11 #endif
13 #ifdef USE_SDL
14 #include <SDL_video.h>
15 #include <SDL_endian.h>
16 #endif
18 #define COMPILE_TIME_ASSERT(condition) typedef char _compile_time_assert__[(condition) ? 1 : -1];
20 #define MAX_INTVAL(int_type) ((((unsigned int_type)(-1))-1)/2)
22 int get_pmask_pixel(struct PMASK *mask, int x, int y) {
23 return 1 & (mask->mask[(mask->h * (x >> MASK_WORD_BITBITS)) + y] >> (x & (MASK_WORD_BITS-1)));
25 void set_pmask_pixel(struct PMASK *mask, int x, int y, int value) {
26 if (value) {
27 mask->mask[(mask->h * (x >> MASK_WORD_BITBITS)) + y] |= 1 << (x & (MASK_WORD_BITS-1));
28 } else {
29 mask->mask[(mask->h * (x >> MASK_WORD_BITBITS)) + y] &=~(1 << (x & (MASK_WORD_BITS-1)));
33 void install_pmask() {
34 COMPILE_TIME_ASSERT((1 << MASK_WORD_BITBITS) == MASK_WORD_BITS);
35 return;
38 void init_pmask (struct PMASK *mask, int w, int h)
40 int words, total_words, x;
42 if ((w > MAX_INTVAL(short int)) || (h > MAX_INTVAL(short int)) || (w < 0) || (h < 0))
44 mask->w = mask->h = 0;
45 #ifndef MASK_SINGLE_MEMORY_BLOCK
46 mask->mask = NULL;
47 #endif
48 return;
51 words = 1 + ((w-1) >> MASK_WORD_BITBITS);
53 total_words = words * h;
55 #ifdef MASK_SINGLE_MEMORY_BLOCK
57 #else
58 mask->mask = (MASK_WORD_TYPE *) malloc(
59 MASK_WORD_SIZE * total_words);
60 if (!mask->mask) {
61 mask->w = mask->h = 0;
62 return;
64 #endif
66 //Now we initialize some of the fields of the structure...
67 mask->w = w;
68 mask->h = h;
70 #ifdef CLEAR_pmask
71 //Now we have a proper mask structure allocated and mostly initialized, but the mask data has garbage! We have to initialize it to 0's:
72 for(x=0; x < total_words; x+=1) {
73 maskt->mask[x] = 0;
75 #else
76 //only clear right hand edge if CLEAR_MASK is not defined
77 for(x=total_words-h; x < total_words; x+=1) {
78 mask->mask[x] = 0;
80 #endif
81 return;
84 void deinit_pmask(struct PMASK *mask) {
85 mask->w = 0;
86 mask->h = 0;
87 #ifndef MASK_SINGLE_MEMORY_BLOCK
88 if (mask->mask) free(mask->mask);
89 mask->mask = NULL;
90 #endif
91 return;
94 void destroy_pmask(struct PMASK *mask) {
95 deinit_pmask(mask);
96 free(mask);
97 return;
100 PMASK *create_pmask (int w, int h) {
101 struct PMASK *maskout;
103 #ifdef MASK_SINGLE_MEMORY_BLOCK
104 int words, total_words;
105 words = 1 + ((w-1) >> MASK_WORD_BITBITS);
106 total_words = words * h;
107 maskout = (PMASK *) malloc(
108 sizeof(PMASK) +
109 MASK_WORD_SIZE * total_words );
110 if(!maskout) return NULL;
111 #else
112 maskout = (PMASK *) malloc(sizeof(PMASK));
113 if(!maskout) return NULL;
114 #endif
116 init_pmask(maskout, w, h);
118 #ifndef MASK_SINGLE_MEMORY_BLOCK
119 if (!maskout->mask) {
120 destroy_pmask(maskout);
121 return NULL;
123 #endif
125 return maskout;
128 void pmask_load_func (struct PMASK *mask, int _x, int _y, void *surface, int trans_color, int (*func)(void*,int,int))
130 int words, x, y, x2, w, h;
131 if(!mask) return;
133 w = mask->w;
134 h = mask->h;
136 words = 1 + ((w-1) >> MASK_WORD_BITBITS);
138 //Now we have to create the bit mask array for the sprite:
139 for(x=0; x < words; x+=1) {
140 for(y=0; y < h; y+=1) {
141 MASK_WORD_TYPE m = 0;
142 for (x2=MASK_WORD_BITS-1; x2 >= 0; x2-=1) {
143 int x3 = (x << MASK_WORD_BITBITS) + x2 + _x;
144 m <<= 1;
145 if ( x3 < w ) {
146 if ( func(surface, x3, y+_y) != trans_color ) {
147 m |= 1;
151 mask->mask[y+x*h] = m;
154 return;
157 void pmask_load_pixels (struct PMASK *mask, void *pixels, int pitch, int bytes_per_pixel, int trans_color)
159 int words, x, y, x2, w, h;
160 if(!mask) return;
162 w = mask->w;
163 h = mask->h;
165 words = 1 + ((w-1) >> MASK_WORD_BITBITS);
167 //Now we have to create the bit mask array for the sprite:
168 for(x=0; x < words; x+=1) {
169 for(y=0; y < h; y+=1) {
170 MASK_WORD_TYPE m = 0;
171 for (x2=MASK_WORD_BITS-1; x2 >= 0; x2-=1) {
172 int x3 = (x << MASK_WORD_BITBITS) + x2;
173 m <<= 1;
174 if ( x3<w ) {
175 //beware of endianness!!!!!!!!!!
176 if ( memcmp(((char*)pixels) + x3 * bytes_per_pixel + y * pitch, &trans_color, bytes_per_pixel) == 0 ) {
177 m |= 1;
181 mask->mask[y+x*h] = m;
184 return;
187 #ifdef USE_ALLEGRO
188 void init_allegro_pmask(struct PMASK *mask, struct BITMAP *sprite) {
189 pmask_load_func (mask, 0, 0, sprite, bitmap_mask_color(sprite), (int (*)(void*,int,int))getpixel);
191 PMASK *create_allegro_pmask(struct BITMAP *sprite) {
192 PMASK *ret;
193 ret = create_pmask(sprite->w, sprite->h);
194 init_allegro_pmask(ret, sprite);
195 return ret;
197 #endif
199 #ifdef USE_SDL
200 static int SDL_getpixel(void *_surface, int x, int y)
202 int bpp = ((SDL_Surface*)_surface)->format->BytesPerPixel;
203 /* Here p is the address to the pixel we want to retrieve */
204 Uint8 *p = (Uint8 *)((SDL_Surface*)_surface)->pixels + y * ((SDL_Surface*)_surface)->pitch + x * bpp;
206 switch(bpp) {
207 case 1:
208 return *p;
210 case 2:
211 return *(Uint16 *)p;
213 case 3:
214 if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
215 return p[0] << 16 | p[1] << 8 | p[2];
216 else
217 return p[0] | p[1] << 8 | p[2] << 16;
219 case 4:
220 return *(Uint32 *)p;
222 default:
223 return 0; /* shouldn't happen, but avoids warnings */
226 void init_sdl_pmask(struct PMASK *mask, struct SDL_Surface *sprite, int trans_color) {
227 pmask_load_func (mask, 0, 0, sprite, trans_color, SDL_getpixel);
229 PMASK *create_sdl_pmask(struct SDL_Surface *sprite, int trans_color) {
230 PMASK *ret;
231 ret = create_pmask(sprite->w, sprite->h);
232 init_sdl_pmask(ret, sprite, trans_color);
233 return ret;
235 #endif
238 int check_pmask_collision(struct PMASK *mask1, struct PMASK *mask2, int x1, int y1, int x2, int y2)
240 int h1, h2, words1, words2, max1, max2;
241 int dx1, dx2, dy1, dy2; //We will use this deltas...
242 int py; //This will store the Y position...
243 int maxh; //This will store the maximum height...
244 int block1, block2;
246 //First we do normal bounding box collision detection...
247 if( !check_bb_collision(mask1, mask2, x1,y1, x2,y2) ) //If there is not bounding box collision...
248 return 0; //return that there is not collision...
250 if (0) { //swap 1 & 2
251 int tmp;
252 PMASK *mtmp;
253 tmp = x1; x1 = x2; x2 = tmp;//swap x
254 tmp = y1; y1 = y2; y2 = tmp;//swap y
255 mtmp = mask1; mask1 = mask2; mask2 = mtmp;//swap masks
258 //First we need to see how much we have to shift the coordinates of the masks...
259 if(x1>x2) {
260 dx1=0; //don't need to shift mask 1.
261 dx2=x1-x2; //shift mask 2 left. Why left? Because we have the mask 1 being on the right of the mask 2, so we have to move mask 2 to the left to do the proper pixel perfect collision...
262 } else {
263 dx1=x2-x1; //shift mask 1 left.
264 dx2=0; //don't need to shift mask 2.
266 if(y1>y2) {
267 dy1=0;
268 dy2=y1-y2; //we need to move this many rows up mask 2. Why up? Because we have mask 1 being down of mask 2, so we have to move mask 2 up to do the proper pixel perfect collision detection...
269 } else {
270 dy1=y2-y1; //we need to move this many rows up mask 1.
271 dy2=0;
274 block1 = dx1>>MASK_WORD_BITBITS;
275 block2 = dx2>>MASK_WORD_BITBITS;
276 dx1 &= MASK_WORD_BITS-1;
277 dx2 &= MASK_WORD_BITS-1;
279 //This will calculate the maximum height that we will reach...
280 if(mask1->h-dy1 > mask2->h-dy2) {
281 maxh=mask2->h-dy2;
282 } else {
283 maxh=mask1->h-dy1;
285 maxh--;
287 h1 = mask1->h;
288 h2 = mask2->h;
289 words1 = 1 + ((mask1->w-1) >> MASK_WORD_BITBITS);
290 words2 = 1 + ((mask2->w-1) >> MASK_WORD_BITBITS);
291 max1 = words1 * h1;
292 max2 = words2 * h2;
293 block1 = block1 * h1 + dy1;
294 block2 = block2 * h2 + dy2;
296 while((block1<max1) && (block2<max2) ) { //search horizantolly in the outer loop
297 for(py=maxh;py>=0;py--) { //Search vertically
298 if(
299 (mask1->mask[py + block1] >> dx1) &
300 (mask2->mask[py + block2] >> dx2)
302 return 1;
304 //Now we have to move to the next block...
305 //we do blocks twice because of the shift
306 if( (!dx1) && (!dx2) ) { //In case both masks are lined up on the x axis...
307 block1 += h1;
308 block2 += h2;
309 } else {
310 if(!dx1) {
311 block2 += h2;
312 dx1 = MASK_WORD_BITS - dx2;
313 dx2 = 0;
314 } else {
315 if(!dx2) {
316 block1 += h1;
317 dx2 = MASK_WORD_BITS - dx1;
318 dx1 = 0;
323 return 0;