1 /*
2  * nazghul - an old-school RPG engine
3  * Copyright (C) 2002, 2003 Gordon McNutt
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
17  * Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Gordon McNutt
20  * gmcnutt@users.sourceforge.net
21  */
22 #include "cmdwin.h"
23 #include "images.h"
24 #include "list.h"
25 #include "map.h"
26 #include "screen.h"
27 #include "session.h"
28 #include "sprite.h"
29 
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
33 
34 /* rsurf - wraps a surface with a reference count so sprites can share them
35  * without fear of premature calls to SDL_FreeSurface(). */
36 struct rsurf {
37         int ref;              /* reference count                     */
38         SDL_Surface *surf;    /* underlying surface                  */
39         char custom : 1;      /* NOT referenced by any struct images */
40 };
41 
42 /* sprite - animation sequence with different facings */
43 struct sprite {
44         char *tag;              /* Script variable name for the sprite.    */
45         int n_frames;           /* per sequence                            */
46         int n_total_frames;     /* n_frames x # facings                    */
47         SDL_Rect *frames;       /* all frames (sequences must be in order) */
48         struct rsurf *rsurf;    /* source of image                         */
49         int facing;             /* current facing sequence                 */
50         int facings;            /* bitmap of supported facing sequences    */
51         int sequence;           /* current animation sequence              */
52         struct sprite *decor;   /* decoration sprites                      */
53         int w_pix, h_pix;       /* frame dimensions (in pixels)            */
54         int faded  : 1;	        /* render sprite sem-transparent           */
55         int wave   : 1;         /* vertical roll                           */
56 };
57 
58 static struct {
59         int ticks_to_next_animation;
60 } Sprite;
61 
62 static int sprite_zoom_factor = 1;
63 static unsigned int sprite_ticks = 0;
64 
rsurf_new(SDL_Surface * surf)65 static struct rsurf *rsurf_new(SDL_Surface *surf)
66 {
67         struct  rsurf *rsurf;
68 
69         rsurf = (struct rsurf*)calloc(1, sizeof(*rsurf));
70         if (!rsurf) {
71                 return 0;
72         }
73         rsurf->ref = 1;
74         rsurf->surf = surf;
75         return rsurf;
76 }
77 
rsurf_unref(struct rsurf * rsurf)78 static void rsurf_unref(struct rsurf *rsurf)
79 {
80         assert(rsurf->ref > 0);
81         rsurf->ref--;
82         if (!rsurf->ref) {
83                 if (rsurf->surf && rsurf->custom) {
84                         SDL_FreeSurface(rsurf->surf);
85                 }
86                 free(rsurf);
87         }
88 }
89 
90 /**
91  * For reasons I don't quite understand, doing an RGBA->RGBA blit with
92  * SDL_BlitSurface() seems to result in a totally transparent image. I wrote
93  * this as a debug measure, but since it works, and I don't expect it to run in
94  * a performance-critical part of the code, I'm leaving it in for now.
95  *
96  * @param source The surface to blit from.
97  * @param from The area of the source to blit from.
98  * @param dest The surface to blit to.
99  * @param to The area of the destination to blit to.
100  */
sprite_custom_blit(SDL_Surface * source,SDL_Rect * from,SDL_Surface * dest,SDL_Rect * to)101 static void sprite_custom_blit(SDL_Surface *source, SDL_Rect *from,
102                                SDL_Surface *dest, SDL_Rect *to)
103 {
104         Uint8 *dpix, *spix, pix = 0;
105         int dx, dy, di, sx,  sy, si, spitch,  dpitch, pix_bytes;
106         Uint8 in_alpha;
107 
108         spix = (Uint8*)(source->pixels);
109         dpix = (Uint8*)(dest->pixels);
110 
111         dpitch = dest->pitch;
112         spitch = source->pitch;
113 
114 	pix_bytes = source->format->BytesPerPixel;
115 	assert(pix_bytes == 1 || pix_bytes == 2 || pix_bytes == 4);
116 	assert(dest->format->BytesPerPixel == pix_bytes);
117 
118         for (dy = 0; dy < from->h; dy++) {
119                 sy = dy;
120                 for (dx = 0; dx < from->w; dx++) {
121                         sx = dx;
122                         di = (dy + to->y) * dpitch + (dx + to->x) * pix_bytes;
123                         si = (sy + from->y) * spitch + (sx + from->x) * pix_bytes;
124 
125 			switch(pix_bytes) {
126 			case 4:	pix = *(Uint32*)spix; break;
127 			case 2: pix = *(Uint16*)spix; break;
128 			case 1:	pix = *(Uint8 *)spix; break;
129 			}
130                         /* Extract the alpha component of the source pixel. */
131                         in_alpha = ((pix & source->format->Amask)
132                                      >> source->format->Ashift);
133 
134                         /* Skip transparent source pixels, leaving destination
135                          * intact. */
136                         if (SDL_ALPHA_TRANSPARENT == in_alpha) {
137                                 continue;
138                         }
139 
140                         /* Do a direct copy of everything else. Note that this
141                          * is only correct if the source alpha is opaque. We
142                          * really should blend semi-transparent source
143                          * pixels. */
144 			switch(pix_bytes) {
145 			case 4:	*(Uint32*)dpix = pix; break;
146 			case 2: *(Uint16*)dpix = pix; break;
147 			case 1:	*(Uint8 *)dpix = pix; break;
148 			}
149                 }
150         }
151 }
152 
153 
154 /**
155  * Replace the sprite's current image surface with a reference-counted copy.
156  *
157  * @param sprite The sprite to modify.
158  * @returns 0 on success or -1 on error. An error occurs if the surface cannot
159  * be copied.
160  */
sprite_clone_and_replace_rsurf(struct sprite * sprite)161 static int sprite_clone_and_replace_rsurf(struct sprite *sprite)
162 {
163         SDL_Surface *dest = 0;
164         SDL_Surface *source = sprite->rsurf->surf;
165         SDL_Rect to;
166         int i;
167 
168         /* Create a new surface so that the original (which may be shared with
169          * other sprites) is not altered. */
170 	dest = SDL_CreateRGBSurface(source->flags,
171                                     sprite->w_pix * sprite->n_total_frames,
172                                     sprite->h_pix,
173                                     source->format->BitsPerPixel,
174                                     source->format->Rmask,
175                                     source->format->Gmask,
176                                     source->format->Bmask,
177                                     source->format->Amask);
178         if (!dest) {
179 		perror_sdl("SDL_CreateRGBSurface");
180 		return -1;
181         }
182 
183         /* Copy each frame of the sprite to the new surface. */
184         to.x = 0;
185         to.y = 0;
186         to.w = sprite->w_pix;
187         to.h = sprite->h_pix;
188         for (i = 0; i < sprite->n_total_frames; i++) {
189                 to.x = i * sprite->w_pix;
190 
191                 /* Blit the frame. */
192                 sprite_custom_blit(sprite->rsurf->surf,
193                             &sprite->frames[i],
194                             dest, &to);
195 
196                 /* Fixup the frames as we go. */
197                 sprite->frames[i] = to;
198         }
199 
200         /* If the original surface was a custom rsurf then unref it. */
201         if (sprite->rsurf->custom) {
202                 rsurf_unref(sprite->rsurf);
203         }
204 
205         /* Stash the surface in a new refcounted surf wrapper. */
206         sprite->rsurf = rsurf_new(dest);
207         sprite->rsurf->custom = 1;
208 
209         return 0;
210 }
211 
sprite_blit_faded(SDL_Surface * source,SDL_Rect * from,SDL_Rect * to)212 static void sprite_blit_faded(SDL_Surface *source, SDL_Rect *from,
213                               SDL_Rect *to)
214 {
215 	int dx, dy, di, sx, sy, si, spitch, dpitch;
216 	Uint32 *dpix, *spix, pixel;
217         Uint8 pix_alpha;
218         SDL_Surface *tmp = 0;
219 
220 	tmp = SDL_CreateRGBSurface(source->flags,
221 				   from->w, from->h,
222 				   source->format->BitsPerPixel,
223 				   source->format->Rmask,
224 				   source->format->Gmask,
225 				   source->format->Bmask,
226 				   source->format->Amask);
227 	if (tmp == NULL) {
228 		perror_sdl("SDL_CreateRGBSurface");
229 		return;
230 	}
231 
232 	dpix = (Uint32 *) tmp->pixels;
233 	spix = (Uint32 *) source->pixels;
234 
235 	dpitch = tmp->pitch / tmp->format->BytesPerPixel;
236 	spitch = source->pitch / source->format->BytesPerPixel;
237 
238 	for (dy = 0; dy < from->h; dy++) {
239 		sy = dy;
240 		for (dx = 0; dx < from->w; dx++) {
241 			sx = dx;
242 			di = (dy * dpitch + dx);
243 			si = (sy + from->y) * spitch + (sx + from->x);
244 
245                         /* Cut alpha component in half. */
246                         pixel = spix[si];
247                         pix_alpha = ((pixel & source->format->Amask)
248                                      >> source->format->Ashift);
249                         pix_alpha /= 2;
250                         pixel &= ~source->format->Amask;
251                         pixel |= (pix_alpha << source->format->Ashift);
252 
253                         /* Assign result. */
254                         dpix[di] = pixel;
255                 }
256         }
257 
258         screenBlit(tmp, NULL, to);
259         SDL_FreeSurface(tmp);
260 }
261 
sprite_paint_wave(struct sprite * sprite,int frame,int x,int y)262 static void sprite_paint_wave(struct sprite *sprite, int frame, int x, int y)
263 {
264 	SDL_Rect src;
265 	SDL_Rect dest;
266         int wavecrest;
267 
268         frame = (frame + sprite_ticks) % sprite->n_frames;
269 
270 	/* Offset the index into the current sequence */
271 	frame += sprite->sequence * sprite->n_frames;
272 
273         // Subtle: when rendering wave sprites zoomed, we'll get artifacts due
274         // to roundoff errors in integer division. Unless we align the
275         // wavecrest to the zoom factor. So for example, if we zoom at a factor
276         // of two then the wavecrest must be a multiple of 2. Since we only
277         // support a zoom factor of 2 right now, the simplest thing to do is
278         // always use 2.
279         wavecrest = (sprite_ticks * 2) % sprite->h_pix;
280         wavecrest = sprite->h_pix - wavecrest; // make it roll south
281 
282 	/* Wave sprites are painted in two blits. The first blit copies
283 	 * everything below the wavecrest to the top part of the onscreen tile.
284 	 * The second blit copies everything above the wavecrest to the
285 	 * bottom part of the onscreen tile. This gives the appearance of a
286 	 * wave rolling over the tile in a direction opposite the wavefront. */
287 
288 	src = sprite->frames[frame];
289 	src.y += wavecrest;	/* fixme -- only works because source
290                                  * image has one column of sprites */
291 	src.h -= wavecrest;
292 
293 	dest.x = x;
294 	dest.y = y;
295 	dest.w = sprite->w_pix;
296 	dest.h = src.h;
297 
298         if (sprite->faded) {
299                 sprite_blit_faded(sprite->rsurf->surf,  &sprite->frames[frame],
300                                   &dest);
301         } else {
302                 screenBlit(sprite->rsurf->surf, &src, &dest);
303         }
304 
305 	src = sprite->frames[frame];
306 	src.h = wavecrest;
307 
308 	dest.x = x;
309 	dest.y = dest.y + (sprite->h_pix - wavecrest) /
310                 sprite_zoom_factor;
311 	dest.w = sprite->w_pix;
312 	dest.h = src.h;
313 
314         if (sprite->faded) {
315                 sprite_blit_faded(sprite->rsurf->surf,  &sprite->frames[frame],
316                                   &dest);
317         } else {
318                 screenBlit(sprite->rsurf->surf, &sprite->frames[frame], &dest);
319         }
320 
321 }
322 
sprite_paint_normal(struct sprite * sprite,int frame,int x,int y)323 static void sprite_paint_normal(struct sprite *sprite, int frame, int x, int y)
324 {
325 	SDL_Rect dest;
326 
327 	dest.x = x;
328 	dest.y = y;
329 	dest.w = sprite->w_pix;
330 	dest.h = sprite->h_pix;
331 
332 #ifndef TEST_PORTRAITS
333         /* dbg hack -- the following code won't work with portraits as sprites;
334          * need a real fix for oversize sprites... */
335         /* If the sprite is larger than a tile, ASSUME (watch out!) we're
336          * blitting a giant character to the map. In this case the bottom of
337          * the sprite will still line up with the bottom of the tile and it
338          * will be horizontally-centered, making the left, right and top
339          * overlap the neighboring tiles. */
340         if (sprite->w_pix > TILE_W) {
341                 dest.x -= (sprite->w_pix - TILE_W) / 2;
342                 dest.y -= (sprite->h_pix - TILE_H);
343         }
344 #endif
345         frame = (frame + sprite_ticks) % sprite->n_frames;
346 	frame += sprite->sequence * sprite->n_frames;
347 
348         if (sprite->faded) {
349                 sprite_blit_faded(sprite->rsurf->surf,  &sprite->frames[frame],
350                                   &dest);
351         } else {
352                 screenBlit(sprite->rsurf->surf, &sprite->frames[frame], &dest);
353         }
354 
355 }
356 
sprite_paint_preframed(struct sprite * sprite,int frame,int x,int y)357 static void sprite_paint_preframed(struct sprite *sprite, int frame, int x, int y)
358 {
359 	SDL_Rect dest;
360 
361 	dest.x = x;
362 	dest.y = y;
363 	dest.w = sprite->w_pix;
364 	dest.h = sprite->h_pix;
365 
366         /* If the sprite is larger than a tile, ASSUME (watch out!) we're
367          * blitting a giant character to the map. In this case the bottom of
368          * the sprite will still line up with the bottom of the tile and it
369          * will be horizontally-centered, making the left, right and top
370          * overlap the neighboring tiles. */
371         if (sprite->w_pix > TILE_W) {
372                 dest.x -= (sprite->w_pix - TILE_W) / 2;
373                 dest.y -= (sprite->h_pix - TILE_H);
374         }
375 
376         frame += sprite->sequence * sprite->n_frames;
377 
378         if (sprite->faded) {
379                 sprite_blit_faded(sprite->rsurf->surf,  &sprite->frames[frame],
380                                   &dest);
381         } else {
382                 screenBlit(sprite->rsurf->surf, &sprite->frames[frame], &dest);
383         }
384 
385 }
386 
sprite_new_internal(int frames,int facings)387 static struct sprite * sprite_new_internal(int frames, int facings)
388 {
389 	struct sprite *sprite;
390 
391 	sprite = (struct sprite*)calloc(1, sizeof(*sprite));
392         if (!sprite)
393                 return 0;
394 
395         sprite->n_frames  = frames;
396         sprite->facing = SPRITE_DEF_FACING;
397         sprite->facings = facings;
398         sprite->n_total_frames = (sprite->n_frames
399                                   * (sprite->facings ?
400                                      NUM_PLANAR_DIRECTIONS : 1));
401 
402 	// Allocate and initialize the rect structures which index into the
403 	// image. One rect per frame of animation. Note that 'facings' is a
404 	// bitmask, not a count. Sprites that don't have different facings
405 	// specify 'facings' as zero, so for these assume we'll want one
406 	// sequence of frames. Sprites that do support facings will need as
407 	// many sequences as there are directions supported by the game.
408 
409 	sprite->frames = (SDL_Rect*)calloc(sprite->n_total_frames,
410                                            sizeof(SDL_Rect));
411         if (!sprite->frames) {
412                 goto abort;
413         }
414 
415 	return sprite;
416 
417  abort:
418         sprite_del(sprite);
419         return 0;
420 }
421 
sprite_del(struct sprite * sprite)422 void sprite_del(struct sprite *sprite)
423 {
424         if (sprite->tag)
425                 free(sprite->tag);
426 	if (sprite->frames)
427 		free(sprite->frames);
428         if (sprite->decor)
429                 sprite_del(sprite->decor);
430         rsurf_unref(sprite->rsurf);
431 
432         free(sprite);
433 }
434 
sprite_paint(struct sprite * sprite,int frame,int x,int y)435 void sprite_paint(struct sprite *sprite, int frame, int x, int y)
436 {
437         while (sprite) {
438 
439                 if (sprite->wave) {
440                         sprite_paint_wave(sprite, frame, x, y);
441                 } else {
442                         sprite_paint_normal(sprite, frame, x, y);
443                 }
444 
445                 sprite = sprite->decor;
446         }
447 }
448 
sprite_paint_frame(struct sprite * sprite,int frame,int x,int y)449 void sprite_paint_frame(struct sprite *sprite, int frame, int x, int y)
450 {
451         while (sprite) {
452 
453                 if (sprite->wave) {
454                         sprite_paint_wave(sprite, frame, x, y);
455                 } else {
456                         sprite_paint_preframed(sprite, frame, x, y);
457                 }
458 
459                 sprite = sprite->decor;
460         }
461 }
462 
sprite_advance_ticks(int ticks)463 void sprite_advance_ticks(int ticks)
464 {
465         Sprite.ticks_to_next_animation -= ticks;
466         if (Sprite.ticks_to_next_animation <= 0) {
467                 sprite_advance_frames();
468                 cmdwin_repaint_cursor();
469                 statusRepaint();
470                 Sprite.ticks_to_next_animation += AnimationTicks;
471         }
472 }
473 
sprite_init(void)474 int sprite_init(void)
475 {
476         Sprite.ticks_to_next_animation = 0;
477         return 0;
478 }
479 
sprite_advance_frames(void)480 void sprite_advance_frames(void)
481 {
482         if (TimeStop) {
483                 Session->time_stop_ticks++;
484         } else {
485                 sprite_ticks++;
486         }
487 	mapSetDirty();
488 
489 }
490 
sprite_get_facing(struct sprite * sprite)491 int sprite_get_facing(struct sprite *sprite)
492 {
493         return sprite->facing;
494 }
495 
sprite_set_facing(struct sprite * sprite,int facing)496 int sprite_set_facing(struct sprite *sprite, int facing)
497 {
498 	int bit, i;
499 
500 	if (facing == SPRITE_DEF_FACING) {
501 		sprite->sequence = 0;
502 		return 0;
503 	}
504 	// facing supported?
505 	if ((sprite->facings & (1 << facing)) == 0) {
506                 dbg("warn: sprite_set_facing: facing=%d invalid for "\
507                     "sprite %s\n",
508                     facing, sprite->tag);
509 		return -1;
510         }
511 
512 	sprite->facing = facing;
513 	sprite->sequence = 0;
514 
515 	// Find the sequence
516 	for (i = 0; i < facing; i++) {
517 		bit = (1 << i);
518 		if (sprite->facings & bit)
519 			sprite->sequence++;
520 	}
521 
522 	return 0;
523 }
524 
sprite_fade(struct sprite * sprite)525 int sprite_fade(struct sprite *sprite)
526 {
527 	sprite->faded = 1;
528 	return 0;
529 }
530 
sprite_unfade(struct sprite * sprite)531 void sprite_unfade(struct sprite *sprite)
532 {
533 	sprite->faded = 0;
534 }
535 
sprite_zoom_out(int factor)536 void sprite_zoom_out(int factor)
537 {
538         sprite_zoom_factor *= factor;
539 }
540 
sprite_zoom_in(int factor)541 extern void sprite_zoom_in(int factor)
542 {
543         sprite_zoom_factor /= factor;
544 }
545 
sprite_new(const char * tag,int frames,int index,int wave,int facings,struct images * images)546 struct sprite * sprite_new(const char *tag, int frames, int index, int wave,
547                            int facings, struct images *images)
548 {
549 	struct sprite *sprite;
550         int col_width;
551         int row_height;
552 	int i;
553         int frame;
554         int col;
555         int row;
556 
557         /* Allocate it. */
558 	sprite = sprite_new_internal(frames, facings);
559         if (!sprite)
560                 return 0;
561 
562         /* Dupe the tag if applicable. */
563         if (tag) {
564                 if (!(sprite->tag = strdup(tag)))
565                         goto abort;
566         }
567 
568         /* Create a new refcounted surf. */
569         if (!(sprite->rsurf = rsurf_new(images->images)))
570                 goto abort;
571 
572 
573         /* Fill out the rest of the basic fields. */
574         sprite->wave = !!wave;
575         sprite->w_pix = images->w;
576         sprite->h_pix = images->h;
577 
578         /* Fill out the frames based on the index and image info. */
579 	col_width = (images->w + images->offx);
580 	row_height = (images->h + images->offy);
581 	for (i = 0, frame = index;
582              i < sprite->n_total_frames;
583              i++, frame++) {
584 		col = frame % images->cols;
585 		row = frame / images->cols;
586 		sprite->frames[i].x = col * col_width + images->offx;
587 		sprite->frames[i].y = row * row_height + images->offy;
588 		sprite->frames[i].w = images->w;
589 		sprite->frames[i].h = images->h;
590 	}
591 
592 	return sprite;
593 
594  abort:
595         sprite_del(sprite);
596         return 0;
597 }
598 
sprite_clone(struct sprite * orig,const char * tag)599 struct sprite *sprite_clone(struct sprite *orig, const char *tag)
600 {
601         SDL_Rect *frames;
602 
603         /* Allocate it. */
604         struct sprite *sprite = sprite_new_internal(orig->n_frames,
605                                                     orig->facings);
606         if (! sprite) {
607                 return 0;
608         }
609 
610         /* Remember the frames pointer before we wipe it out with the copy. */
611         frames = sprite->frames;
612 
613         /* Copy the sprite info. */
614         memcpy(sprite, orig, sizeof(*orig));
615 
616         /* Copy the frames. */
617         sprite->frames = frames;
618         memcpy(sprite->frames, orig->frames,
619                sprite->n_total_frames * sizeof(sprite->frames[0]));
620 
621         /* Bump the refcount on the surface. */
622         sprite->rsurf->ref++;
623 
624         /* Dupe the tag if applicable. */
625         if (tag) {
626                 sprite->tag = strdup(tag);
627         } else if (orig->tag) {
628                 sprite->tag = strdup(orig->tag);
629         }
630 
631         return sprite;
632 }
633 
sprite_append_decoration(struct sprite * base,struct sprite * decor)634 void sprite_append_decoration(struct sprite *base, struct sprite *decor)
635 {
636         assert(base);
637         while (base->decor) {
638                 base = base->decor;
639         }
640         base->decor = sprite_clone(decor, decor->tag);
641 }
642 
sprite_get_tag(struct sprite * sprite)643 char *sprite_get_tag(struct sprite *sprite)
644 {
645         return sprite->tag;
646 }
647 
sprite_is_faded(struct sprite * sprite)648 int sprite_is_faded(struct sprite *sprite)
649 {
650         return sprite->faded;
651 }
652 
sprite_can_face(struct sprite * sprite,int facing)653 int sprite_can_face(struct sprite *sprite, int facing)
654 {
655         return (sprite->facings & (1 << facing));
656 }
657 
658 /* sprite_save - save to file for reload. */
sprite_save(struct sprite * sprite,struct save * save)659 void sprite_save(struct sprite *sprite, struct save *save)
660 {
661         /* For simple sprites just save the tag. */
662         if (!sprite->decor) {
663                 assert(sprite->tag);
664                 save->write(save, "%s ; sprite\n", sprite->tag);
665                 return;
666         }
667 
668         /* For composite sprites */
669         save->write(save, ("(mk-composite-sprite (list "));
670         while (sprite) {
671                 assert(sprite->tag);
672                 save->append(save, "%s ", sprite->tag);
673                 sprite = sprite->decor;
674         }
675         save->append(save, ")) ; composite sprite\n");
676 }
677 
sprite_apply_matrix_to_image(SDL_Surface * source,SDL_Rect * from,SDL_Surface * dest,SDL_Rect * to,float matrix[4][3])678 static void sprite_apply_matrix_to_image(SDL_Surface *source, SDL_Rect *from,
679                                          SDL_Surface *dest,  SDL_Rect *to,
680                                          float matrix[4][3])
681 {
682         Uint8 *dpix, *spix, out_pix, in_pix = 0;
683         int dx, dy, di, sx,  sy, si, spitch,  dpitch, sbytes, dbytes;
684         Uint8 in_red, in_grn, in_blu, in_alpha, out_red, out_grn, out_blu,
685                 out_alpha;
686         int ired, igrn, iblu;
687         Uint32 transparent;
688 
689         spix = (Uint8*)(source->pixels);
690         dpix = (Uint8*)(dest->pixels);
691 
692         dpitch = dest->pitch;
693         spitch = source->pitch;
694 
695 	sbytes = source->format->BytesPerPixel;
696 	assert(sbytes == 1 || sbytes == 2 || sbytes == 4);
697 	dbytes = dest->format->BytesPerPixel;
698 	assert(dbytes == 1 || dbytes == 2 || dbytes == 4);
699 
700         /* Make a transparent pixel. If SDL_ALPHA_TRANSPARENT is non-zero that
701          * means transparency is high, so use the mask value. Otherwise zero
702          * means transparent. */
703         if (SDL_ALPHA_TRANSPARENT) {
704                 transparent = dest->format->Amask;
705         } else {
706                 transparent = 0;
707         }
708 
709         for (dy = 0; dy < from->h; dy++) {
710                 sy = dy;
711                 for (dx = 0; dx < from->w; dx++) {
712                         sx = dx;
713                         di = (dy + to->y) * dpitch + (dx + to->x) * dbytes;
714                         si = (sy + from->y) * spitch + (sx + from->x) * sbytes;
715 
716 			switch(sbytes) {
717 			case 4:	in_pix = *(Uint32*)spix; break;
718 			case 2: in_pix = *(Uint16*)spix; break;
719 			case 1:	in_pix = *(Uint8 *)spix; break;
720 			}
721                         /* Extract the alpha component of the source pixel. */
722                         in_alpha = ((in_pix & source->format->Amask)
723                                      >> source->format->Ashift);
724 
725                         /* For speed, skip transparent pixels. */
726                         if (SDL_ALPHA_TRANSPARENT == in_alpha) {
727 				switch(sbytes) {
728 				case 4:	*(Uint32*)dpix = transparent; break;
729 				case 2: *(Uint16*)dpix = transparent; break;
730 				case 1:	*(Uint8 *)dpix = transparent; break;
731 				}
732                                 continue;
733                         }
734 
735                         /* Extract the color components of the source pixel. */
736                         in_red = ((in_pix & source->format->Rmask)
737                                    >> source->format->Rshift);
738                         in_grn = ((in_pix & source->format->Gmask)
739                                    >> source->format->Gshift);
740                         in_blu = ((in_pix & source->format->Bmask)
741                                    >> source->format->Bshift);
742 
743                         /* Run the matrix conversion. */
744                         ired = (int)((in_red * matrix[0][0])
745                                      + (in_grn * matrix[0][1])
746                                      + (in_blu * matrix[0][2])
747                                      + matrix[3][0]);
748 
749                         igrn = (int)((in_red * matrix[1][0])
750                                      + (in_grn * matrix[1][1])
751                                      + (in_blu * matrix[1][2])
752                                      + matrix[3][1]);
753 
754                         iblu = (int)((in_red * matrix[2][0])
755                                      + (in_grn * matrix[2][1])
756                                      + (in_blu * matrix[2][2])
757                                      + matrix[3][2]);
758 
759                         out_alpha = in_alpha;
760 
761                         /* Clamp the result to the allowed range. */
762                         out_red = clamp(ired, 0,
763                                         (int)(dest->format->Rmask
764                                               >> dest->format->Rshift));
765                         out_grn = clamp(igrn, 0,
766                                         (int)(dest->format->Gmask
767                                               >> dest->format->Gshift));
768                         out_blu = clamp(iblu, 0,
769                                         (int)(dest->format->Bmask
770                                               >> dest->format->Bshift));
771 
772                         /* Recombine them, along with the original alpha
773                          * component, into the destination pixel. */
774                         out_pix = (out_red << dest->format->Rshift
775                                     | out_grn << dest->format->Gshift
776                                     | out_blu << dest->format->Bshift
777                                     | out_alpha << dest->format->Ashift);
778 			switch(sbytes) {
779 			case 4:	*(Uint32*)dpix = out_pix; break;
780 			case 2: *(Uint16*)dpix = out_pix; break;
781 			case 1:	*(Uint8 *)dpix = out_pix; break;
782 			}
783                 }
784         }
785 }
786 
787 /* sprite_apply_matrix - applies a color conversion matrix. This is good for
788  * converting monotone or grayscale images into other tones. The matrix is
789  * applied as follows:
790  *
791  * r = R*m[0][0] + G*m[0][1] + B*m[0][2] + m[3][0]
792  * g = R*m[1][0] + G*m[1][1] + B*m[1][2] + m[3][1]
793  * b = R*m[2][0] + G*m[2][1] + B*m[2][2] + m[3][2]
794  *
795  */
sprite_apply_matrix(struct sprite * sprite,float matrix[4][3])796 void sprite_apply_matrix(struct sprite *sprite, float matrix[4][3])
797 {
798         SDL_Surface *dest = 0;
799         SDL_Surface *source = sprite->rsurf->surf;
800         SDL_Rect to;
801         int i;
802 
803         /* Create a new surface so that the original (which may be shared with
804          * other sprites) is not altered. */
805 	dest = SDL_CreateRGBSurface(source->flags,
806                                     sprite->w_pix * sprite->n_total_frames,
807                                     sprite->h_pix,
808                                     source->format->BitsPerPixel,
809                                     source->format->Rmask,
810                                     source->format->Gmask,
811                                     source->format->Bmask,
812                                     source->format->Amask);
813         if (!dest) {
814 		perror_sdl("SDL_CreateRGBSurface");
815 		return;
816         }
817 
818         /* Apply the correction to each frame of the sprite. This could be
819          * optimized a bit by applying the matrix in one pass to the entire set
820          * of frames, since they are always contiguous in the image. */
821         to.x = 0;
822         to.y = 0;
823         to.w = sprite->w_pix;
824         to.h = sprite->h_pix;
825         for (i = 0; i < sprite->n_total_frames; i++) {
826                 to.x = i * sprite->w_pix;
827 
828                 /* Apply to the image. */
829                 sprite_apply_matrix_to_image(sprite->rsurf->surf,
830                                              &sprite->frames[i],
831                                              dest, &to, matrix);
832 
833                 /* Fixup the frames as we go. */
834                 sprite->frames[i] = to;
835         }
836 
837         /* Stash the surface in a new refcounted surf wrapper. */
838         sprite->rsurf = rsurf_new(dest);
839         sprite->rsurf->custom = 1;
840 }
841 
sprite_strip_decorations(struct sprite * sprite)842 void sprite_strip_decorations(struct sprite *sprite)
843 {
844         if (sprite->decor) {
845                 /* Decoration sprites are always single-referenced clones, so
846                  * blow them away when they're stripped. This will recursively
847                  * delete all the trailing decor sprites. */
848                 sprite_del(sprite->decor);
849                 sprite->decor = 0;
850         }
851 }
852 
sprite_blit_over(struct sprite * dest,struct sprite * src)853 void sprite_blit_over(struct sprite *dest, struct sprite *src)
854 {
855         int i = 0;
856 
857         /* Check preconditions. */
858         assert(dest->w_pix == src->w_pix);
859         assert(dest->h_pix == src->h_pix);
860         assert(dest->n_total_frames == src->n_total_frames);
861 
862         /* Clone the destination sprite's surface before changing it. */
863         if (sprite_clone_and_replace_rsurf(dest))
864                 return;
865 
866         /* For each frame... */
867         for (i = 0; i < dest->n_total_frames; i++) {
868 
869                 /* Blit the source over the destination. */
870                 sprite_custom_blit(src->rsurf->surf, &src->frames[i],
871                                    dest->rsurf->surf, &dest->frames[i]);
872 
873         }
874 }
875 
sprite_num_frames(struct sprite * sprite)876 int sprite_num_frames(struct sprite *sprite)
877 {
878 	return sprite->n_frames;
879 }
880 
sprite_facings_list(struct sprite * sprite)881 int sprite_facings_list(struct sprite *sprite)
882 {
883 	return sprite->facings;
884 }
885 
sprite_paint_direct(struct sprite * sprite,int frame,SDL_Rect * dest)886 void sprite_paint_direct(struct sprite *sprite, int frame, SDL_Rect *dest)
887 {
888 	screenBlit(sprite->rsurf->surf, &sprite->frames[frame], dest);
889 }
890