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 "video/sdl/sdl_renderer.hpp"
24 #include "util/log.hpp"
25 #include "math/random_generator.hpp"
34 static Uint32 get_pixel_mapping (SDL_Surface *src, void *pixel)
38 switch (src->format->BytesPerPixel)
41 mapped = *((Uint8 *) pixel);
44 mapped = *((Uint16 *) pixel);
48 Uint8 *tmp = (Uint8 *) pixel;
49 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
50 mapped |= tmp[0] << 16;
51 mapped |= tmp[1] << 8;
52 mapped |= tmp[2] << 0;
54 mapped |= tmp[0] << 0;
55 mapped |= tmp[1] << 8;
56 mapped |= tmp[2] << 16;
61 mapped = *((Uint32 *) pixel);
65 log_warning << "Unknown BytesPerPixel value: "
66 << src->format->BytesPerPixel << std::endl;
71 } /* Uint32 get_pixel_mapping */
73 static Uint32 get_random_color (SDL_Surface *src)
77 r = (Uint32) graphicsRandom.rand ();
78 /* rand() returns 31bit random numbers. So call it twice to get full 32 bit. */
80 r |= (Uint32) graphicsRandom.rand ();
82 switch (src->format->BytesPerPixel)
98 } /* Uint32 get_random_color */
100 static bool color_is_used (SDL_Surface *src, Uint32 color)
102 if(SDL_MUSTLOCK(src))
103 SDL_LockSurface(src);
105 for(int y = 0; y < src->h; y++) {
106 for(int x = 0; x < src->w; x++) {
107 Uint8 *pixel = (Uint8 *) src->pixels
108 + (y * src->pitch) + (x * src->format->BytesPerPixel);
109 Uint32 mapped = get_pixel_mapping (src, pixel);
117 } /* bool color_is_used */
119 static Uint32 get_unused_color (SDL_Surface *src)
125 random_color = get_random_color (src);
126 } while (color_is_used (src, random_color));
128 return (random_color);
129 } /* Uint32 get_unused_color */
133 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
135 if(numerator == denominator)
142 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);
143 int bpp = dst->format->BytesPerPixel;
144 if(SDL_MUSTLOCK(src))
146 SDL_LockSurface(src);
148 if(SDL_MUSTLOCK(dst))
150 SDL_LockSurface(dst);
152 for(int y = 0;y < dst->h;y++) {
153 for(int x = 0;x < dst->w;x++) {
154 Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
155 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
158 dstpixel[3] = srcpixel[3];
160 dstpixel[2] = srcpixel[2];
162 dstpixel[1] = srcpixel[1];
164 dstpixel[0] = srcpixel[0];
168 if(SDL_MUSTLOCK(dst))
170 SDL_UnlockSurface(dst);
172 if(SDL_MUSTLOCK(src))
174 SDL_UnlockSurface(src);
176 if(!src->format->Amask)
178 if(src->flags & SDL_SRCALPHA)
180 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
182 if(src->flags & SDL_SRCCOLORKEY)
184 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
194 void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
196 int bpp = src->format->BytesPerPixel;
205 Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
206 Uint32 mapped = get_pixel_mapping (src, srcpixel);
207 SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
210 void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
212 color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
213 color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
214 color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
215 color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
218 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
220 if(numerator == denominator)
227 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);
228 int bpp = dst->format->BytesPerPixel;
229 if(SDL_MUSTLOCK(src))
231 SDL_LockSurface(src);
233 if(SDL_MUSTLOCK(dst))
235 SDL_LockSurface(dst);
237 for(int y = 0;y < dst->h;y++) {
238 for(int x = 0;x < dst->w;x++) {
239 int srcx = x * denominator / numerator;
240 int srcy = y * denominator / numerator;
241 Uint8 color00[4], color01[4], color10[4], color11[4];
242 getpixel(src, srcx, srcy, color00);
243 getpixel(src, srcx + 1, srcy, color01);
244 getpixel(src, srcx, srcy + 1, color10);
245 getpixel(src, srcx + 1, srcy + 1, color11);
246 Uint8 color0[4], color1[4], color[4];
247 int remx = x * denominator % numerator;
248 merge(color0, color00, color01, remx, numerator);
249 merge(color1, color10, color11, remx, numerator);
250 int remy = y * denominator % numerator;
251 merge(color, color0, color1, remy, numerator);
252 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
253 Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
259 *(Uint16 *)dstpixel = mapped;
262 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
263 dstpixel[0] = (mapped >> 16) & 0xff;
264 dstpixel[1] = (mapped >> 8) & 0xff;
265 dstpixel[2] = (mapped >> 0) & 0xff;
267 dstpixel[0] = (mapped >> 0) & 0xff;
268 dstpixel[1] = (mapped >> 8) & 0xff;
269 dstpixel[2] = (mapped >> 16) & 0xff;
273 *(Uint32 *)dstpixel = mapped;
278 if(SDL_MUSTLOCK(dst))
280 SDL_UnlockSurface(dst);
282 if(SDL_MUSTLOCK(src))
284 SDL_UnlockSurface(src);
286 if(!src->format->Amask)
289 if(src->flags & SDL_SRCALPHA)
291 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
293 if(src->flags & SDL_SRCCOLORKEY)
295 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
304 SDL_Surface *horz_flip(SDL_Surface *src)
306 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);
307 int bpp = dst->format->BytesPerPixel;
308 if(SDL_MUSTLOCK(src))
310 SDL_LockSurface(src);
312 if(SDL_MUSTLOCK(dst))
314 SDL_LockSurface(dst);
316 for(int y = 0;y < dst->h;y++) {
317 for(int x = 0;x < dst->w;x++) {
318 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
319 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
322 dstpixel[3] = srcpixel[3];
324 dstpixel[2] = srcpixel[2];
326 dstpixel[1] = srcpixel[1];
328 dstpixel[0] = srcpixel[0];
332 if(SDL_MUSTLOCK(dst))
334 SDL_UnlockSurface(dst);
336 if(SDL_MUSTLOCK(src))
338 SDL_UnlockSurface(src);
340 if(!src->format->Amask)
343 if(src->flags & SDL_SRCALPHA)
345 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
347 if(src->flags & SDL_SRCCOLORKEY)
349 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
356 SDL_Surface *vert_flip(SDL_Surface *src)
358 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);
359 int bpp = dst->format->BytesPerPixel;
360 if(SDL_MUSTLOCK(src))
362 SDL_LockSurface(src);
364 if(SDL_MUSTLOCK(dst))
366 SDL_LockSurface(dst);
368 for(int y = 0;y < dst->h;y++) {
369 for(int x = 0;x < dst->w;x++) {
370 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
371 Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
374 dstpixel[3] = srcpixel[3];
376 dstpixel[2] = srcpixel[2];
378 dstpixel[1] = srcpixel[1];
380 dstpixel[0] = srcpixel[0];
384 if(SDL_MUSTLOCK(dst))
386 SDL_UnlockSurface(dst);
388 if(SDL_MUSTLOCK(src))
390 SDL_UnlockSurface(src);
393 if(!src->format->Amask)
395 if(src->flags & SDL_SRCALPHA)
397 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
399 if(src->flags & SDL_SRCCOLORKEY)
401 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
408 SDL_Surface *colorize(SDL_Surface *src, const Color &color)
410 // FIXME: This is really slow
411 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);
412 int bpp = dst->format->BytesPerPixel;
413 if(SDL_MUSTLOCK(src))
415 SDL_LockSurface(src);
417 if(SDL_MUSTLOCK(dst))
419 SDL_LockSurface(dst);
421 for(int y = 0;y < dst->h;y++) {
422 for(int x = 0;x < dst->w;x++) {
423 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
424 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
431 mapped = *(Uint16 *)srcpixel;
434 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
435 mapped |= srcpixel[0] << 16;
436 mapped |= srcpixel[1] << 8;
437 mapped |= srcpixel[2] << 0;
439 mapped |= srcpixel[0] << 0;
440 mapped |= srcpixel[1] << 8;
441 mapped |= srcpixel[2] << 16;
445 mapped = *(Uint32 *)srcpixel;
449 if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
451 assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
452 int red = (int) (color.red * 256);
453 int green = (int) (color.green * 256);
454 int blue = (int) (color.blue * 256);
457 SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
458 mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
466 *(Uint16 *)dstpixel = mapped;
469 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
470 dstpixel[0] = (mapped >> 16) & 0xff;
471 dstpixel[1] = (mapped >> 8) & 0xff;
472 dstpixel[2] = (mapped >> 0) & 0xff;
474 dstpixel[0] = (mapped >> 0) & 0xff;
475 dstpixel[1] = (mapped >> 8) & 0xff;
476 dstpixel[2] = (mapped >> 16) & 0xff;
480 *(Uint32 *)dstpixel = mapped;
485 if(SDL_MUSTLOCK(dst))
487 SDL_UnlockSurface(dst);
489 if(SDL_MUSTLOCK(src))
491 SDL_UnlockSurface(src);
493 if(!src->format->Amask)
496 if(src->flags & SDL_SRCALPHA)
498 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
500 if(src->flags & SDL_SRCCOLORKEY)
502 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
510 /** Optimizes a SDL_Surface surface and returns it in the "display format". If
511 * the surface does not have an alpha channel, simply calls
512 * "SDL_DisplayFormat". If the surface has an alpha channel, examines all the
513 * pixels. If in fact semi-transparent pixels are found, calls
514 * "SDL_DisplayFormatAlpha". If only fully transparent and fully opaque pixels
515 * are found, converts the surface to a 1-bit alpha surface with
516 * "SDL_SetColorKey". */
517 SDL_Surface *optimize(SDL_Surface *src)
520 bool have_transparent = false;
521 bool have_semi_trans = false;
522 bool have_opaque = false;
524 if(!src->format->Amask)
525 return SDL_DisplayFormat(src);
527 if(SDL_MUSTLOCK(src))
528 SDL_LockSurface(src);
530 /* Iterate over all the pixels and record which ones we found. */
531 for(int y = 0; y < src->h; y++) {
532 for(int x = 0; x < src->w; x++) {
533 Uint8 *pixel = (Uint8 *) src->pixels
534 + (y * src->pitch) + (x * src->format->BytesPerPixel);
535 Uint32 mapped = get_pixel_mapping (src, pixel);
536 Uint8 red, green, blue, alpha;
537 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
540 have_transparent = true;
541 else if (alpha > 240)
544 have_semi_trans = true;
548 if(SDL_MUSTLOCK(src))
549 SDL_UnlockSurface(src);
552 return SDL_DisplayFormatAlpha(src);
554 if (!have_transparent /* && !have_semi_trans */)
555 return SDL_DisplayFormat(src);
557 /* The surface is totally transparent. We shouldn't return a surface at all,
558 * but since the calling code can't cope with that, use the alpha channel in
560 if (!have_opaque /* && !have_semi_trans */)
561 return SDL_DisplayFormatAlpha(src);
563 /* If we get here, the surface has fully transparent pixels and fully opaque
564 * pixels, but no semi-transparent pixels. We can therefore use a one bit
565 * transparency, which is pretty fast to draw. This code path is a bit bulky
566 * and rarely used (~25 surfaces when starting the game and entering a
567 * level), so it could be removed for readabilities sake. -octo */
569 /* Create a new surface without alpha channel */
570 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA),
571 src->w, src->h, src->format->BitsPerPixel,
572 src->format->Rmask, src->format->Gmask, src->format->Bmask, /* Amask = */ 0);
573 /* Get a color that's not in the source surface. It is used to mark
574 * transparent pixels. There's a possible race condition: Maybe we should
575 * lock the surface before calling this function and add a "bool have_lock"
576 * argument to "get_unused_color"? -octo */
577 Uint32 color_key = get_unused_color (src);
579 if(SDL_MUSTLOCK(src))
580 SDL_LockSurface(src);
581 if(SDL_MUSTLOCK(dst))
582 SDL_LockSurface(dst);
584 /* Copy all the pixels to the new surface */
585 for(int y = 0; y < src->h; y++) {
586 for(int x = 0; x < src->w; x++) {
587 Uint8 *src_pixel = (Uint8 *) src->pixels
588 + (y * src->pitch) + (x * src->format->BytesPerPixel);
589 Uint8 *dst_pixel = (Uint8 *) dst->pixels
590 + (y * dst->pitch) + (x * dst->format->BytesPerPixel);
591 Uint32 mapped = get_pixel_mapping (src, src_pixel);
592 Uint8 red, green, blue, alpha;
593 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
595 /* "alpha" should either be smaller than 16 or greater than 240. We
596 * unlocked the surface in between though, so use 128 to play it save,
597 * i.e. don't leave any unspecified code paths. */
601 switch (dst->format->BytesPerPixel)
604 *dst_pixel = (Uint8) mapped;
608 *((Uint16 *) dst_pixel) = (Uint16) mapped;
612 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
613 dst_pixel[0] = (mapped >> 16) & 0xff;
614 dst_pixel[1] = (mapped >> 8) & 0xff;
615 dst_pixel[2] = (mapped >> 0) & 0xff;
617 dst_pixel[0] = (mapped >> 0) & 0xff;
618 dst_pixel[1] = (mapped >> 8) & 0xff;
619 dst_pixel[2] = (mapped >> 16) & 0xff;
624 *((Uint32 *) dst_pixel) = mapped;
625 } /* switch (dst->format->BytesPerPixel) */
629 if(SDL_MUSTLOCK(src))
630 SDL_UnlockSurface(src);
631 if(SDL_MUSTLOCK(dst))
632 SDL_UnlockSurface(dst);
634 /* Tell SDL that the "color_key" color is supposed to be transparent. */
635 SDL_SetColorKey (dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, color_key);
636 SDL_Surface *convert = SDL_DisplayFormat(dst);
637 SDL_FreeSurface(dst);
642 } /* SDL_Surface *optimize */
646 SDLTexture::SDLTexture(SDL_Surface* image) :
649 texture = SDL_CreateTextureFromSurface(static_cast<SDLRenderer*>(Renderer::instance())->get_sdl_renderer(),
653 std::ostringstream msg;
654 msg << "couldn't create texture: " << SDL_GetError();
655 throw std::runtime_error(msg.str());
664 //FIXME: float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
665 //FIXME: float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
667 if(xfactor < yfactor)
669 numerator = config->screenwidth;
670 denominator = SCREEN_WIDTH;
674 numerator = config->screenheight;
675 denominator = SCREEN_HEIGHT;
678 cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
682 SDLTexture::~SDLTexture()
684 SDL_DestroyTexture(texture);
689 SDLTexture::get_transform(const Color &color, DrawingEffect effect)
691 if(cache[NO_EFFECT][color] == 0) {
692 assert(cache[NO_EFFECT][Color::WHITE]);
693 cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
695 if(cache[effect][color] == 0) {
696 assert(cache[NO_EFFECT][color]);
700 case HORIZONTAL_FLIP:
701 cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
704 cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
710 return cache[effect][color];
714 /* vim: set sw=2 sts=2 et : */