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"
32 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
34 if(numerator == denominator)
41 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);
42 int bpp = dst->format->BytesPerPixel;
51 for(int y = 0;y < dst->h;y++) {
52 for(int x = 0;x < dst->w;x++) {
53 Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
54 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
57 dstpixel[3] = srcpixel[3];
59 dstpixel[2] = srcpixel[2];
61 dstpixel[1] = srcpixel[1];
63 dstpixel[0] = srcpixel[0];
69 SDL_UnlockSurface(dst);
73 SDL_UnlockSurface(src);
75 if(!src->format->Amask)
77 if(src->flags & SDL_SRCALPHA)
79 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
81 if(src->flags & SDL_SRCCOLORKEY)
83 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
91 static void surface_copy_alpha (SDL_Surface *dst, SDL_Surface *src)
93 #if SDL_VERSION_ATLEAST(1,3,0)
98 status = SDL_GetSurfaceAlphaMod (src, &alpha);
100 SDL_SetSurfaceAlphaMod (dst, alpha);
102 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
106 static void surface_copy_colorkey (SDL_Surface *dst, SDL_Surface *src)
108 #if SDL_VERSION_ATLEAST(1,3,0)
113 /* Returns non-zero if color key is disabled. */
114 status = SDL_GetColorKey (src, &colorkey);
116 SDL_SetColorKey (dst, /* enabled = */ 1, colorkey);
118 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
123 void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
125 int bpp = src->format->BytesPerPixel;
134 Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
141 mapped = *(Uint16 *)srcpixel;
144 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
145 mapped |= srcpixel[0] << 16;
146 mapped |= srcpixel[1] << 8;
147 mapped |= srcpixel[2] << 0;
149 mapped |= srcpixel[0] << 0;
150 mapped |= srcpixel[1] << 8;
151 mapped |= srcpixel[2] << 16;
155 mapped = *(Uint32 *)srcpixel;
158 SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
161 void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
163 color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
164 color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
165 color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
166 color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
169 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
171 if(numerator == denominator)
178 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);
179 int bpp = dst->format->BytesPerPixel;
180 if(SDL_MUSTLOCK(src))
182 SDL_LockSurface(src);
184 if(SDL_MUSTLOCK(dst))
186 SDL_LockSurface(dst);
188 for(int y = 0;y < dst->h;y++) {
189 for(int x = 0;x < dst->w;x++) {
190 int srcx = x * denominator / numerator;
191 int srcy = y * denominator / numerator;
192 Uint8 color00[4], color01[4], color10[4], color11[4];
193 getpixel(src, srcx, srcy, color00);
194 getpixel(src, srcx + 1, srcy, color01);
195 getpixel(src, srcx, srcy + 1, color10);
196 getpixel(src, srcx + 1, srcy + 1, color11);
197 Uint8 color0[4], color1[4], color[4];
198 int remx = x * denominator % numerator;
199 merge(color0, color00, color01, remx, numerator);
200 merge(color1, color10, color11, remx, numerator);
201 int remy = y * denominator % numerator;
202 merge(color, color0, color1, remy, numerator);
203 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
204 Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
210 *(Uint16 *)dstpixel = mapped;
213 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
214 dstpixel[0] = (mapped >> 16) & 0xff;
215 dstpixel[1] = (mapped >> 8) & 0xff;
216 dstpixel[2] = (mapped >> 0) & 0xff;
218 dstpixel[0] = (mapped >> 0) & 0xff;
219 dstpixel[1] = (mapped >> 8) & 0xff;
220 dstpixel[2] = (mapped >> 16) & 0xff;
224 *(Uint32 *)dstpixel = mapped;
229 if(SDL_MUSTLOCK(dst))
231 SDL_UnlockSurface(dst);
233 if(SDL_MUSTLOCK(src))
235 SDL_UnlockSurface(src);
237 if(!src->format->Amask)
239 if(src->flags & SDL_SRCALPHA)
241 surface_copy_alpha (dst, src);
243 if(src->flags & SDL_SRCCOLORKEY)
245 surface_copy_colorkey (dst, src);
251 #endif /* BILINEAR */
253 SDL_Surface *horz_flip(SDL_Surface *src)
255 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);
256 int bpp = dst->format->BytesPerPixel;
257 if(SDL_MUSTLOCK(src))
259 SDL_LockSurface(src);
261 if(SDL_MUSTLOCK(dst))
263 SDL_LockSurface(dst);
265 for(int y = 0;y < dst->h;y++) {
266 for(int x = 0;x < dst->w;x++) {
267 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
268 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
271 dstpixel[3] = srcpixel[3];
273 dstpixel[2] = srcpixel[2];
275 dstpixel[1] = srcpixel[1];
277 dstpixel[0] = srcpixel[0];
281 if(SDL_MUSTLOCK(dst))
283 SDL_UnlockSurface(dst);
285 if(SDL_MUSTLOCK(src))
287 SDL_UnlockSurface(src);
289 if(!src->format->Amask)
291 if(src->flags & SDL_SRCALPHA)
293 surface_copy_alpha (dst, src);
295 if(src->flags & SDL_SRCCOLORKEY)
297 surface_copy_colorkey (dst, src);
303 SDL_Surface *vert_flip(SDL_Surface *src)
305 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);
306 int bpp = dst->format->BytesPerPixel;
307 if(SDL_MUSTLOCK(src))
309 SDL_LockSurface(src);
311 if(SDL_MUSTLOCK(dst))
313 SDL_LockSurface(dst);
315 for(int y = 0;y < dst->h;y++) {
316 for(int x = 0;x < dst->w;x++) {
317 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
318 Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
321 dstpixel[3] = srcpixel[3];
323 dstpixel[2] = srcpixel[2];
325 dstpixel[1] = srcpixel[1];
327 dstpixel[0] = srcpixel[0];
331 if(SDL_MUSTLOCK(dst))
333 SDL_UnlockSurface(dst);
335 if(SDL_MUSTLOCK(src))
337 SDL_UnlockSurface(src);
339 if(!src->format->Amask)
341 if(src->flags & SDL_SRCALPHA)
343 surface_copy_alpha (dst, src);
345 if(src->flags & SDL_SRCCOLORKEY)
347 surface_copy_colorkey (dst, src);
353 SDL_Surface *colorize(SDL_Surface *src, const Color &color)
355 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);
356 #if SDL_VERSION_ATLEAST(1,3,0)
357 assert ((color.red >= 0) && (color.red <= 1.0));
358 assert ((color.green >= 0) && (color.green <= 1.0));
359 assert ((color.blue >= 0) && (color.blue <= 1.0));
360 assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
361 Uint8 red = (Uint8) (color.red * 255.0);
362 Uint8 green = (Uint8) (color.green * 255.0);
363 Uint8 blue = (Uint8) (color.blue * 255.0);
365 SDL_SetSurfaceColorMod (dst, red, green, blue);
367 // FIXME: This is really slow
368 assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
369 int red = (int) (color.red * 256);
370 int green = (int) (color.green * 256);
371 int blue = (int) (color.blue * 256);
372 int bpp = dst->format->BytesPerPixel;
373 if(SDL_MUSTLOCK(src))
375 SDL_LockSurface(src);
377 if(SDL_MUSTLOCK(dst))
379 SDL_LockSurface(dst);
381 for(int y = 0;y < dst->h;y++) {
382 for(int x = 0;x < dst->w;x++) {
383 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
384 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
391 mapped = *(Uint16 *)srcpixel;
394 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
395 mapped |= srcpixel[0] << 16;
396 mapped |= srcpixel[1] << 8;
397 mapped |= srcpixel[2] << 0;
399 mapped |= srcpixel[0] << 0;
400 mapped |= srcpixel[1] << 8;
401 mapped |= srcpixel[2] << 16;
405 mapped = *(Uint32 *)srcpixel;
408 if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
411 SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
412 mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
419 *(Uint16 *)dstpixel = mapped;
422 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
423 dstpixel[0] = (mapped >> 16) & 0xff;
424 dstpixel[1] = (mapped >> 8) & 0xff;
425 dstpixel[2] = (mapped >> 0) & 0xff;
427 dstpixel[0] = (mapped >> 0) & 0xff;
428 dstpixel[1] = (mapped >> 8) & 0xff;
429 dstpixel[2] = (mapped >> 16) & 0xff;
433 *(Uint32 *)dstpixel = mapped;
438 #endif /* SDL version < 1.3.0 */
439 if(SDL_MUSTLOCK(dst))
441 SDL_UnlockSurface(dst);
443 if(SDL_MUSTLOCK(src))
445 SDL_UnlockSurface(src);
447 if(!src->format->Amask)
449 if(src->flags & SDL_SRCALPHA)
451 surface_copy_alpha (dst, src);
453 if(src->flags & SDL_SRCCOLORKEY)
455 surface_copy_colorkey (dst, src);
461 SDL_Surface *optimize(SDL_Surface *src)
463 if(!src->format->Amask)
465 return SDL_DisplayFormat(src);
470 bool colors[(1 << 12)];
471 memset(colors, 0, (1 << 12) * sizeof(bool));
475 int semitransparent = 0;
477 int squaredalphasum = 0;
479 int bpp = src->format->BytesPerPixel;
480 if(SDL_MUSTLOCK(src))
482 SDL_LockSurface(src);
484 for(int y = 0;y < src->h;y++) {
485 for(int x = 0;x < src->w;x++) {
486 Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
493 mapped = *(Uint16 *)pixel;
496 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
497 mapped |= pixel[0] << 16;
498 mapped |= pixel[1] << 8;
499 mapped |= pixel[2] << 0;
501 mapped |= pixel[0] << 0;
502 mapped |= pixel[1] << 8;
503 mapped |= pixel[2] << 16;
507 mapped = *(Uint32 *)pixel;
510 Uint8 red, green, blue, alpha;
511 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
516 else if (alpha > 240)
520 squaredalphasum += alpha * alpha;
525 squaredalphasum += alpha * alpha;
529 colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] = true;
533 if(SDL_MUSTLOCK(src))
535 SDL_UnlockSurface(src);
537 int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
538 int avgsquaredalpha = (opaque + semitransparent) ? squaredalphasum / (opaque + semitransparent) : 0;
539 int alphavariance = avgsquaredalpha - avgalpha * avgalpha;
540 if(semitransparent > ((transparent + opaque + semitransparent) / 8) && alphavariance > 16)
542 return SDL_DisplayFormatAlpha(src);
546 for(int i = 0;i < (1 << 12);i++)
555 return SDL_DisplayFormatAlpha(src);
557 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA), src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, 0);
558 bpp = dst->format->BytesPerPixel;
559 Uint32 key = SDL_MapRGB(dst->format, (((keycolor & 0xf00) >> 4) | 0xf), ((keycolor & 0xf0) | 0xf), (((keycolor & 0xf) << 4) | 0xf));
560 if(SDL_MUSTLOCK(src))
562 SDL_LockSurface(src);
564 if(SDL_MUSTLOCK(dst))
566 SDL_LockSurface(dst);
568 for(int y = 0;y < dst->h;y++) {
569 for(int x = 0;x < dst->w;x++) {
570 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
571 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
578 mapped = *(Uint16 *)srcpixel;
581 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
582 mapped |= srcpixel[0] << 16;
583 mapped |= srcpixel[1] << 8;
584 mapped |= srcpixel[2] << 0;
586 mapped |= srcpixel[0] << 0;
587 mapped |= srcpixel[1] << 8;
588 mapped |= srcpixel[2] << 16;
592 mapped = *(Uint32 *)srcpixel;
595 Uint8 red, green, blue, alpha;
596 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
597 //if(alpha < (avgalpha / 4))
604 mapped = SDL_MapRGB(dst->format, red, green, blue);
611 *(Uint16 *)dstpixel = mapped;
614 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
615 dstpixel[0] = (mapped >> 16) & 0xff;
616 dstpixel[1] = (mapped >> 8) & 0xff;
617 dstpixel[2] = (mapped >> 0) & 0xff;
619 dstpixel[0] = (mapped >> 0) & 0xff;
620 dstpixel[1] = (mapped >> 8) & 0xff;
621 dstpixel[2] = (mapped >> 16) & 0xff;
625 *(Uint32 *)dstpixel = mapped;
630 if(SDL_MUSTLOCK(dst))
632 SDL_UnlockSurface(dst);
634 if(SDL_MUSTLOCK(src))
636 SDL_UnlockSurface(src);
641 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, avgalpha);
644 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
645 SDL_Surface *convert = SDL_DisplayFormat(dst);
646 SDL_FreeSurface(dst);
652 SDLTexture::SDLTexture(SDL_Surface* image) :
655 texture = optimize(image);
656 //width = texture->w;
657 //height = texture->h;
660 //FIXME: float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
661 //FIXME: float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
663 if(xfactor < yfactor)
665 numerator = config->screenwidth;
666 denominator = SCREEN_WIDTH;
670 numerator = config->screenheight;
671 denominator = SCREEN_HEIGHT;
674 cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
677 SDLTexture::~SDLTexture()
679 SDL_FreeSurface(texture);
683 SDLTexture::get_transform(const Color &color, DrawingEffect effect)
685 if(cache[NO_EFFECT][color] == 0) {
686 assert(cache[NO_EFFECT][Color::WHITE]);
687 cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
689 if(cache[effect][color] == 0) {
690 assert(cache[NO_EFFECT][color]);
694 case HORIZONTAL_FLIP:
695 cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
698 cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
704 return cache[effect][color];