OSDN Git Service

日本語版
[nazghul-jp/nazghul-jp.git] / src / sprite.c
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
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
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  */
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  */
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
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
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
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
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
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
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
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
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
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
474 int sprite_init(void)
475 {
476         Sprite.ticks_to_next_animation = 0;
477         return 0;
478 }
479
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
491 int sprite_get_facing(struct sprite *sprite)
492 {
493         return sprite->facing;
494 }
495
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
525 int sprite_fade(struct sprite *sprite)
526 {
527         sprite->faded = 1;
528         return 0;
529 }
530
531 void sprite_unfade(struct sprite *sprite)
532 {
533         sprite->faded = 0;
534 }
535
536 void sprite_zoom_out(int factor)
537 {
538         sprite_zoom_factor *= factor;
539 }
540
541 extern void sprite_zoom_in(int factor)
542 {
543         sprite_zoom_factor /= factor;
544 }
545
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
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
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
643 char *sprite_get_tag(struct sprite *sprite)
644 {
645         return sprite->tag;
646 }
647
648 int sprite_is_faded(struct sprite *sprite)
649 {
650         return sprite->faded;
651 }
652
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. */
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
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  */
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
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
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
876 int sprite_num_frames(struct sprite *sprite)
877 {
878         return sprite->n_frames;        
879 }
880
881 int sprite_facings_list(struct sprite *sprite)
882 {
883         return sprite->facings; 
884 }
885
886 void sprite_paint_direct(struct sprite *sprite, int frame, SDL_Rect *dest)
887 {
888         screenBlit(sprite->rsurf->surf, &sprite->frames[frame], dest);
889 }