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