2 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "supertux/gameconfig.hpp"
20 #include "supertux/globals.hpp"
21 #include "video/color.hpp"
22 #include "video/sdl/sdl_texture.hpp"
23 #include "util/log.hpp"
24 #include "math/random_generator.hpp"
33 static Uint32 get_pixel_mapping (SDL_Surface *src, void *pixel)
37 switch (src->format->BytesPerPixel)
40 mapped = *((Uint8 *) pixel);
43 mapped = *((Uint16 *) pixel);
47 Uint8 *tmp = (Uint8 *) pixel;
48 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
49 mapped |= tmp[0] << 16;
50 mapped |= tmp[1] << 8;
51 mapped |= tmp[2] << 0;
53 mapped |= tmp[0] << 0;
54 mapped |= tmp[1] << 8;
55 mapped |= tmp[2] << 16;
60 mapped = *((Uint32 *) pixel);
64 log_warning << "Unknown BytesPerPixel value: "
65 << src->format->BytesPerPixel << std::endl;
70 } /* Uint32 get_pixel_mapping */
72 static Uint32 get_random_color (SDL_Surface *src)
76 r = (Uint32) graphicsRandom.rand ();
77 /* rand() returns 31bit random numbers. So call it twice to get full 32 bit. */
79 r |= (Uint32) graphicsRandom.rand ();
81 switch (src->format->BytesPerPixel)
97 } /* Uint32 get_random_color */
99 static bool color_is_used (SDL_Surface *src, Uint32 color)
101 if(SDL_MUSTLOCK(src))
102 SDL_LockSurface(src);
104 for(int y = 0; y < src->h; y++) {
105 for(int x = 0; x < src->w; x++) {
106 Uint8 *pixel = (Uint8 *) src->pixels
107 + (y * src->pitch) + (x * src->format->BytesPerPixel);
108 Uint32 mapped = get_pixel_mapping (src, pixel);
116 } /* bool color_is_used */
118 static Uint32 get_unused_color (SDL_Surface *src)
124 random_color = get_random_color (src);
125 } while (color_is_used (src, random_color));
127 return (random_color);
128 } /* Uint32 get_unused_color */
131 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
133 if(numerator == denominator)
140 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
141 int bpp = dst->format->BytesPerPixel;
142 if(SDL_MUSTLOCK(src))
144 SDL_LockSurface(src);
146 if(SDL_MUSTLOCK(dst))
148 SDL_LockSurface(dst);
150 for(int y = 0;y < dst->h;y++) {
151 for(int x = 0;x < dst->w;x++) {
152 Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
153 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
156 dstpixel[3] = srcpixel[3];
158 dstpixel[2] = srcpixel[2];
160 dstpixel[1] = srcpixel[1];
162 dstpixel[0] = srcpixel[0];
166 if(SDL_MUSTLOCK(dst))
168 SDL_UnlockSurface(dst);
170 if(SDL_MUSTLOCK(src))
172 SDL_UnlockSurface(src);
174 if(!src->format->Amask)
176 if(src->flags & SDL_SRCALPHA)
178 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
180 if(src->flags & SDL_SRCCOLORKEY)
182 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
191 void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
193 int bpp = src->format->BytesPerPixel;
202 Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
203 Uint32 mapped = get_pixel_mapping (src, srcpixel);
204 SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
207 void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
209 color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
210 color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
211 color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
212 color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
215 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
217 if(numerator == denominator)
224 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
225 int bpp = dst->format->BytesPerPixel;
226 if(SDL_MUSTLOCK(src))
228 SDL_LockSurface(src);
230 if(SDL_MUSTLOCK(dst))
232 SDL_LockSurface(dst);
234 for(int y = 0;y < dst->h;y++) {
235 for(int x = 0;x < dst->w;x++) {
236 int srcx = x * denominator / numerator;
237 int srcy = y * denominator / numerator;
238 Uint8 color00[4], color01[4], color10[4], color11[4];
239 getpixel(src, srcx, srcy, color00);
240 getpixel(src, srcx + 1, srcy, color01);
241 getpixel(src, srcx, srcy + 1, color10);
242 getpixel(src, srcx + 1, srcy + 1, color11);
243 Uint8 color0[4], color1[4], color[4];
244 int remx = x * denominator % numerator;
245 merge(color0, color00, color01, remx, numerator);
246 merge(color1, color10, color11, remx, numerator);
247 int remy = y * denominator % numerator;
248 merge(color, color0, color1, remy, numerator);
249 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
250 Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
256 *(Uint16 *)dstpixel = mapped;
259 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
260 dstpixel[0] = (mapped >> 16) & 0xff;
261 dstpixel[1] = (mapped >> 8) & 0xff;
262 dstpixel[2] = (mapped >> 0) & 0xff;
264 dstpixel[0] = (mapped >> 0) & 0xff;
265 dstpixel[1] = (mapped >> 8) & 0xff;
266 dstpixel[2] = (mapped >> 16) & 0xff;
270 *(Uint32 *)dstpixel = mapped;
275 if(SDL_MUSTLOCK(dst))
277 SDL_UnlockSurface(dst);
279 if(SDL_MUSTLOCK(src))
281 SDL_UnlockSurface(src);
283 if(!src->format->Amask)
285 if(src->flags & SDL_SRCALPHA)
287 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
289 if(src->flags & SDL_SRCCOLORKEY)
291 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
299 SDL_Surface *horz_flip(SDL_Surface *src)
301 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
302 int bpp = dst->format->BytesPerPixel;
303 if(SDL_MUSTLOCK(src))
305 SDL_LockSurface(src);
307 if(SDL_MUSTLOCK(dst))
309 SDL_LockSurface(dst);
311 for(int y = 0;y < dst->h;y++) {
312 for(int x = 0;x < dst->w;x++) {
313 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
314 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
317 dstpixel[3] = srcpixel[3];
319 dstpixel[2] = srcpixel[2];
321 dstpixel[1] = srcpixel[1];
323 dstpixel[0] = srcpixel[0];
327 if(SDL_MUSTLOCK(dst))
329 SDL_UnlockSurface(dst);
331 if(SDL_MUSTLOCK(src))
333 SDL_UnlockSurface(src);
335 if(!src->format->Amask)
337 if(src->flags & SDL_SRCALPHA)
339 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
341 if(src->flags & SDL_SRCCOLORKEY)
343 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
349 SDL_Surface *vert_flip(SDL_Surface *src)
351 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
352 int bpp = dst->format->BytesPerPixel;
353 if(SDL_MUSTLOCK(src))
355 SDL_LockSurface(src);
357 if(SDL_MUSTLOCK(dst))
359 SDL_LockSurface(dst);
361 for(int y = 0;y < dst->h;y++) {
362 for(int x = 0;x < dst->w;x++) {
363 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
364 Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
367 dstpixel[3] = srcpixel[3];
369 dstpixel[2] = srcpixel[2];
371 dstpixel[1] = srcpixel[1];
373 dstpixel[0] = srcpixel[0];
377 if(SDL_MUSTLOCK(dst))
379 SDL_UnlockSurface(dst);
381 if(SDL_MUSTLOCK(src))
383 SDL_UnlockSurface(src);
385 if(!src->format->Amask)
387 if(src->flags & SDL_SRCALPHA)
389 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
391 if(src->flags & SDL_SRCCOLORKEY)
393 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
399 SDL_Surface *colorize(SDL_Surface *src, const Color &color)
401 // FIXME: This is really slow
402 assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
403 int red = (int) (color.red * 256);
404 int green = (int) (color.green * 256);
405 int blue = (int) (color.blue * 256);
406 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
407 int bpp = dst->format->BytesPerPixel;
408 if(SDL_MUSTLOCK(src))
410 SDL_LockSurface(src);
412 if(SDL_MUSTLOCK(dst))
414 SDL_LockSurface(dst);
416 for(int y = 0;y < dst->h;y++) {
417 for(int x = 0;x < dst->w;x++) {
418 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
419 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
426 mapped = *(Uint16 *)srcpixel;
429 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
430 mapped |= srcpixel[0] << 16;
431 mapped |= srcpixel[1] << 8;
432 mapped |= srcpixel[2] << 0;
434 mapped |= srcpixel[0] << 0;
435 mapped |= srcpixel[1] << 8;
436 mapped |= srcpixel[2] << 16;
440 mapped = *(Uint32 *)srcpixel;
443 if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
446 SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
447 mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
454 *(Uint16 *)dstpixel = mapped;
457 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
458 dstpixel[0] = (mapped >> 16) & 0xff;
459 dstpixel[1] = (mapped >> 8) & 0xff;
460 dstpixel[2] = (mapped >> 0) & 0xff;
462 dstpixel[0] = (mapped >> 0) & 0xff;
463 dstpixel[1] = (mapped >> 8) & 0xff;
464 dstpixel[2] = (mapped >> 16) & 0xff;
468 *(Uint32 *)dstpixel = mapped;
473 if(SDL_MUSTLOCK(dst))
475 SDL_UnlockSurface(dst);
477 if(SDL_MUSTLOCK(src))
479 SDL_UnlockSurface(src);
481 if(!src->format->Amask)
483 if(src->flags & SDL_SRCALPHA)
485 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
487 if(src->flags & SDL_SRCCOLORKEY)
489 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
495 /** Optimizes a SDL_Surface surface and returns it in the "display format". If
496 * the surface does not have an alpha channel, simply calls
497 * "SDL_DisplayFormat". If the surface has an alpha channel, examines all the
498 * pixels. If in fact semi-transparent pixels are found, calls
499 * "SDL_DisplayFormatAlpha". If only fully transparent and fully opaque pixels
500 * are found, converts the surface to a 1-bit alpha surface with
501 * "SDL_SetColorKey". */
502 SDL_Surface *optimize(SDL_Surface *src)
504 bool have_transparent = false;
505 bool have_semi_trans = false;
506 bool have_opaque = false;
508 if(!src->format->Amask)
509 return SDL_DisplayFormat(src);
511 if(SDL_MUSTLOCK(src))
512 SDL_LockSurface(src);
514 /* Iterate over all the pixels and record which ones we found. */
515 for(int y = 0; y < src->h; y++) {
516 for(int x = 0; x < src->w; x++) {
517 Uint8 *pixel = (Uint8 *) src->pixels
518 + (y * src->pitch) + (x * src->format->BytesPerPixel);
519 Uint32 mapped = get_pixel_mapping (src, pixel);
520 Uint8 red, green, blue, alpha;
521 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
524 have_transparent = true;
525 else if (alpha > 240)
528 have_semi_trans = true;
532 if(SDL_MUSTLOCK(src))
533 SDL_UnlockSurface(src);
536 return SDL_DisplayFormatAlpha(src);
538 if (!have_transparent /* && !have_semi_trans */)
539 return SDL_DisplayFormat(src);
541 /* The surface is totally transparent. We shouldn't return a surface at all,
542 * but since the calling code can't cope with that, use the alpha channel in
544 if (!have_opaque /* && !have_semi_trans */)
545 return SDL_DisplayFormatAlpha(src);
547 /* If we get here, the surface has fully transparent pixels and fully opaque
548 * pixels, but no semi-transparent pixels. We can therefore use a one bit
549 * transparency, which is pretty fast to draw. This code path is a bit bulky
550 * and rarely used (~25 surfaces when starting the game and entering a
551 * level), so it could be removed for readabilities sake. -octo */
553 /* Create a new surface without alpha channel */
554 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA),
555 src->w, src->h, src->format->BitsPerPixel,
556 src->format->Rmask, src->format->Gmask, src->format->Bmask, /* Amask = */ 0);
557 /* Get a color that's not in the source surface. It is used to mark
558 * transparent pixels. There's a possible race condition: Maybe we should
559 * lock the surface before calling this function and add a "bool have_lock"
560 * argument to "get_unused_color"? -octo */
561 Uint32 color_key = get_unused_color (src);
563 if(SDL_MUSTLOCK(src))
564 SDL_LockSurface(src);
565 if(SDL_MUSTLOCK(dst))
566 SDL_LockSurface(dst);
568 /* Copy all the pixels to the new surface */
569 for(int y = 0; y < src->h; y++) {
570 for(int x = 0; x < src->w; x++) {
571 Uint8 *src_pixel = (Uint8 *) src->pixels
572 + (y * src->pitch) + (x * src->format->BytesPerPixel);
573 Uint8 *dst_pixel = (Uint8 *) dst->pixels
574 + (y * dst->pitch) + (x * dst->format->BytesPerPixel);
575 Uint32 mapped = get_pixel_mapping (src, src_pixel);
576 Uint8 red, green, blue, alpha;
577 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
579 /* "alpha" should either be smaller than 16 or greater than 240. We
580 * unlocked the surface in between though, so use 128 to play it save,
581 * i.e. don't leave any unspecified code paths. */
585 switch (dst->format->BytesPerPixel)
588 *dst_pixel = (Uint8) mapped;
592 *((Uint16 *) dst_pixel) = (Uint16) mapped;
596 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
597 dst_pixel[0] = (mapped >> 16) & 0xff;
598 dst_pixel[1] = (mapped >> 8) & 0xff;
599 dst_pixel[2] = (mapped >> 0) & 0xff;
601 dst_pixel[0] = (mapped >> 0) & 0xff;
602 dst_pixel[1] = (mapped >> 8) & 0xff;
603 dst_pixel[2] = (mapped >> 16) & 0xff;
608 *((Uint32 *) dst_pixel) = mapped;
609 } /* switch (dst->format->BytesPerPixel) */
613 if(SDL_MUSTLOCK(src))
614 SDL_UnlockSurface(src);
615 if(SDL_MUSTLOCK(dst))
616 SDL_UnlockSurface(dst);
618 /* Tell SDL that the "color_key" color is supposed to be transparent. */
619 SDL_SetColorKey (dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, color_key);
620 SDL_Surface *convert = SDL_DisplayFormat(dst);
621 SDL_FreeSurface(dst);
623 } /* SDL_Surface *optimize */
627 SDLTexture::SDLTexture(SDL_Surface* image) :
630 texture = optimize(image);
631 //width = texture->w;
632 //height = texture->h;
635 //FIXME: float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
636 //FIXME: float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
638 if(xfactor < yfactor)
640 numerator = config->screenwidth;
641 denominator = SCREEN_WIDTH;
645 numerator = config->screenheight;
646 denominator = SCREEN_HEIGHT;
649 cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
652 SDLTexture::~SDLTexture()
654 SDL_FreeSurface(texture);
658 SDLTexture::get_transform(const Color &color, DrawingEffect effect)
660 if(cache[NO_EFFECT][color] == 0) {
661 assert(cache[NO_EFFECT][Color::WHITE]);
662 cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
664 if(cache[effect][color] == 0) {
665 assert(cache[NO_EFFECT][color]);
669 case HORIZONTAL_FLIP:
670 cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
673 cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
679 return cache[effect][color];
682 /* vim: set sw=2 sts=2 et : */