00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include <config.h>
00018
00019 #include "supertux/gameconfig.hpp"
00020 #include "supertux/globals.hpp"
00021 #include "video/color.hpp"
00022 #include "video/sdl/sdl_texture.hpp"
00023 #include "util/log.hpp"
00024 #include "math/random_generator.hpp"
00025
00026 #include <assert.h>
00027
00028 #include <SDL.h>
00029
00030 namespace {
00031 #define BILINEAR
00032
00033 static Uint32 get_pixel_mapping (SDL_Surface *src, void *pixel)
00034 {
00035 Uint32 mapped = 0;
00036
00037 switch (src->format->BytesPerPixel)
00038 {
00039 case 1:
00040 mapped = *((Uint8 *) pixel);
00041 break;
00042 case 2:
00043 mapped = *((Uint16 *) pixel);
00044 break;
00045 case 3:
00046 {
00047 Uint8 *tmp = (Uint8 *) pixel;
00048 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00049 mapped |= tmp[0] << 16;
00050 mapped |= tmp[1] << 8;
00051 mapped |= tmp[2] << 0;
00052 #else
00053 mapped |= tmp[0] << 0;
00054 mapped |= tmp[1] << 8;
00055 mapped |= tmp[2] << 16;
00056 #endif
00057 break;
00058 }
00059 case 4:
00060 mapped = *((Uint32 *) pixel);
00061 break;
00062
00063 default:
00064 log_warning << "Unknown BytesPerPixel value: "
00065 << src->format->BytesPerPixel << std::endl;
00066 mapped = 0;
00067 }
00068
00069 return (mapped);
00070 }
00071
00072 static Uint32 get_random_color (SDL_Surface *src)
00073 {
00074 Uint32 r;
00075
00076 r = (Uint32) graphicsRandom.rand ();
00077
00078 r <<= 1;
00079 r |= (Uint32) graphicsRandom.rand ();
00080
00081 switch (src->format->BytesPerPixel)
00082 {
00083 case 1:
00084 r &= 0x000000ff;
00085 break;
00086
00087 case 2:
00088 r &= 0x0000ffff;
00089 break;
00090
00091 case 3:
00092 r &= 0x0000ffff;
00093 break;
00094 }
00095
00096 return (r);
00097 }
00098
00099 static bool color_is_used (SDL_Surface *src, Uint32 color)
00100 {
00101 if(SDL_MUSTLOCK(src))
00102 SDL_LockSurface(src);
00103
00104 for(int y = 0; y < src->h; y++) {
00105 for(int x = 0; x < src->w; x++) {
00106 Uint8 *pixel = (Uint8 *) src->pixels
00107 + (y * src->pitch) + (x * src->format->BytesPerPixel);
00108 Uint32 mapped = get_pixel_mapping (src, pixel);
00109
00110 if (color == mapped)
00111 return (true);
00112 }
00113 }
00114
00115 return (false);
00116 }
00117
00118 static Uint32 get_unused_color (SDL_Surface *src)
00119 {
00120 Uint32 random_color;
00121
00122 do
00123 {
00124 random_color = get_random_color (src);
00125 } while (color_is_used (src, random_color));
00126
00127 return (random_color);
00128 }
00129
00130 #ifdef NAIVE
00131 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
00132 {
00133 if(numerator == denominator)
00134 {
00135 src->refcount++;
00136 return src;
00137 }
00138 else
00139 {
00140 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);
00141 int bpp = dst->format->BytesPerPixel;
00142 if(SDL_MUSTLOCK(src))
00143 {
00144 SDL_LockSurface(src);
00145 }
00146 if(SDL_MUSTLOCK(dst))
00147 {
00148 SDL_LockSurface(dst);
00149 }
00150 for(int y = 0;y < dst->h;y++) {
00151 for(int x = 0;x < dst->w;x++) {
00152 Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
00153 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
00154 switch(bpp) {
00155 case 4:
00156 dstpixel[3] = srcpixel[3];
00157 case 3:
00158 dstpixel[2] = srcpixel[2];
00159 case 2:
00160 dstpixel[1] = srcpixel[1];
00161 case 1:
00162 dstpixel[0] = srcpixel[0];
00163 }
00164 }
00165 }
00166 if(SDL_MUSTLOCK(dst))
00167 {
00168 SDL_UnlockSurface(dst);
00169 }
00170 if(SDL_MUSTLOCK(src))
00171 {
00172 SDL_UnlockSurface(src);
00173 }
00174 if(!src->format->Amask)
00175 {
00176 if(src->flags & SDL_SRCALPHA)
00177 {
00178 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
00179 }
00180 if(src->flags & SDL_SRCCOLORKEY)
00181 {
00182 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
00183 }
00184 }
00185 return dst;
00186 }
00187 }
00188 #endif
00189
00190 #ifdef BILINEAR
00191 void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
00192 {
00193 int bpp = src->format->BytesPerPixel;
00194 if(srcx == src->w)
00195 {
00196 srcx--;
00197 }
00198 if(srcy == src->h)
00199 {
00200 srcy--;
00201 }
00202 Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
00203 Uint32 mapped = get_pixel_mapping (src, srcpixel);
00204 SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
00205 }
00206
00207 void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
00208 {
00209 color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
00210 color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
00211 color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
00212 color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
00213 }
00214
00215 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
00216 {
00217 if(numerator == denominator)
00218 {
00219 src->refcount++;
00220 return src;
00221 }
00222 else
00223 {
00224 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);
00225 int bpp = dst->format->BytesPerPixel;
00226 if(SDL_MUSTLOCK(src))
00227 {
00228 SDL_LockSurface(src);
00229 }
00230 if(SDL_MUSTLOCK(dst))
00231 {
00232 SDL_LockSurface(dst);
00233 }
00234 for(int y = 0;y < dst->h;y++) {
00235 for(int x = 0;x < dst->w;x++) {
00236 int srcx = x * denominator / numerator;
00237 int srcy = y * denominator / numerator;
00238 Uint8 color00[4], color01[4], color10[4], color11[4];
00239 getpixel(src, srcx, srcy, color00);
00240 getpixel(src, srcx + 1, srcy, color01);
00241 getpixel(src, srcx, srcy + 1, color10);
00242 getpixel(src, srcx + 1, srcy + 1, color11);
00243 Uint8 color0[4], color1[4], color[4];
00244 int remx = x * denominator % numerator;
00245 merge(color0, color00, color01, remx, numerator);
00246 merge(color1, color10, color11, remx, numerator);
00247 int remy = y * denominator % numerator;
00248 merge(color, color0, color1, remy, numerator);
00249 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
00250 Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
00251 switch(bpp) {
00252 case 1:
00253 *dstpixel = mapped;
00254 break;
00255 case 2:
00256 *(Uint16 *)dstpixel = mapped;
00257 break;
00258 case 3:
00259 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00260 dstpixel[0] = (mapped >> 16) & 0xff;
00261 dstpixel[1] = (mapped >> 8) & 0xff;
00262 dstpixel[2] = (mapped >> 0) & 0xff;
00263 #else
00264 dstpixel[0] = (mapped >> 0) & 0xff;
00265 dstpixel[1] = (mapped >> 8) & 0xff;
00266 dstpixel[2] = (mapped >> 16) & 0xff;
00267 #endif
00268 break;
00269 case 4:
00270 *(Uint32 *)dstpixel = mapped;
00271 break;
00272 }
00273 }
00274 }
00275 if(SDL_MUSTLOCK(dst))
00276 {
00277 SDL_UnlockSurface(dst);
00278 }
00279 if(SDL_MUSTLOCK(src))
00280 {
00281 SDL_UnlockSurface(src);
00282 }
00283 if(!src->format->Amask)
00284 {
00285 if(src->flags & SDL_SRCALPHA)
00286 {
00287 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
00288 }
00289 if(src->flags & SDL_SRCCOLORKEY)
00290 {
00291 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
00292 }
00293 }
00294 return dst;
00295 }
00296 }
00297 #endif
00298
00299 SDL_Surface *horz_flip(SDL_Surface *src)
00300 {
00301 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);
00302 int bpp = dst->format->BytesPerPixel;
00303 if(SDL_MUSTLOCK(src))
00304 {
00305 SDL_LockSurface(src);
00306 }
00307 if(SDL_MUSTLOCK(dst))
00308 {
00309 SDL_LockSurface(dst);
00310 }
00311 for(int y = 0;y < dst->h;y++) {
00312 for(int x = 0;x < dst->w;x++) {
00313 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
00314 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
00315 switch(bpp) {
00316 case 4:
00317 dstpixel[3] = srcpixel[3];
00318 case 3:
00319 dstpixel[2] = srcpixel[2];
00320 case 2:
00321 dstpixel[1] = srcpixel[1];
00322 case 1:
00323 dstpixel[0] = srcpixel[0];
00324 }
00325 }
00326 }
00327 if(SDL_MUSTLOCK(dst))
00328 {
00329 SDL_UnlockSurface(dst);
00330 }
00331 if(SDL_MUSTLOCK(src))
00332 {
00333 SDL_UnlockSurface(src);
00334 }
00335 if(!src->format->Amask)
00336 {
00337 if(src->flags & SDL_SRCALPHA)
00338 {
00339 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
00340 }
00341 if(src->flags & SDL_SRCCOLORKEY)
00342 {
00343 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
00344 }
00345 }
00346 return dst;
00347 }
00348
00349 SDL_Surface *vert_flip(SDL_Surface *src)
00350 {
00351 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);
00352 int bpp = dst->format->BytesPerPixel;
00353 if(SDL_MUSTLOCK(src))
00354 {
00355 SDL_LockSurface(src);
00356 }
00357 if(SDL_MUSTLOCK(dst))
00358 {
00359 SDL_LockSurface(dst);
00360 }
00361 for(int y = 0;y < dst->h;y++) {
00362 for(int x = 0;x < dst->w;x++) {
00363 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
00364 Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
00365 switch(bpp) {
00366 case 4:
00367 dstpixel[3] = srcpixel[3];
00368 case 3:
00369 dstpixel[2] = srcpixel[2];
00370 case 2:
00371 dstpixel[1] = srcpixel[1];
00372 case 1:
00373 dstpixel[0] = srcpixel[0];
00374 }
00375 }
00376 }
00377 if(SDL_MUSTLOCK(dst))
00378 {
00379 SDL_UnlockSurface(dst);
00380 }
00381 if(SDL_MUSTLOCK(src))
00382 {
00383 SDL_UnlockSurface(src);
00384 }
00385 if(!src->format->Amask)
00386 {
00387 if(src->flags & SDL_SRCALPHA)
00388 {
00389 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
00390 }
00391 if(src->flags & SDL_SRCCOLORKEY)
00392 {
00393 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
00394 }
00395 }
00396 return dst;
00397 }
00398
00399 SDL_Surface *colorize(SDL_Surface *src, const Color &color)
00400 {
00401
00402 assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
00403 int red = (int) (color.red * 256);
00404 int green = (int) (color.green * 256);
00405 int blue = (int) (color.blue * 256);
00406 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);
00407 int bpp = dst->format->BytesPerPixel;
00408 if(SDL_MUSTLOCK(src))
00409 {
00410 SDL_LockSurface(src);
00411 }
00412 if(SDL_MUSTLOCK(dst))
00413 {
00414 SDL_LockSurface(dst);
00415 }
00416 for(int y = 0;y < dst->h;y++) {
00417 for(int x = 0;x < dst->w;x++) {
00418 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
00419 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
00420 Uint32 mapped = 0;
00421 switch(bpp) {
00422 case 1:
00423 mapped = *srcpixel;
00424 break;
00425 case 2:
00426 mapped = *(Uint16 *)srcpixel;
00427 break;
00428 case 3:
00429 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00430 mapped |= srcpixel[0] << 16;
00431 mapped |= srcpixel[1] << 8;
00432 mapped |= srcpixel[2] << 0;
00433 #else
00434 mapped |= srcpixel[0] << 0;
00435 mapped |= srcpixel[1] << 8;
00436 mapped |= srcpixel[2] << 16;
00437 #endif
00438 break;
00439 case 4:
00440 mapped = *(Uint32 *)srcpixel;
00441 break;
00442 }
00443 if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
00444 {
00445 Uint8 r, g, b, a;
00446 SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
00447 mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
00448 }
00449 switch(bpp) {
00450 case 1:
00451 *dstpixel = mapped;
00452 break;
00453 case 2:
00454 *(Uint16 *)dstpixel = mapped;
00455 break;
00456 case 3:
00457 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00458 dstpixel[0] = (mapped >> 16) & 0xff;
00459 dstpixel[1] = (mapped >> 8) & 0xff;
00460 dstpixel[2] = (mapped >> 0) & 0xff;
00461 #else
00462 dstpixel[0] = (mapped >> 0) & 0xff;
00463 dstpixel[1] = (mapped >> 8) & 0xff;
00464 dstpixel[2] = (mapped >> 16) & 0xff;
00465 #endif
00466 break;
00467 case 4:
00468 *(Uint32 *)dstpixel = mapped;
00469 break;
00470 }
00471 }
00472 }
00473 if(SDL_MUSTLOCK(dst))
00474 {
00475 SDL_UnlockSurface(dst);
00476 }
00477 if(SDL_MUSTLOCK(src))
00478 {
00479 SDL_UnlockSurface(src);
00480 }
00481 if(!src->format->Amask)
00482 {
00483 if(src->flags & SDL_SRCALPHA)
00484 {
00485 SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
00486 }
00487 if(src->flags & SDL_SRCCOLORKEY)
00488 {
00489 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
00490 }
00491 }
00492 return dst;
00493 }
00494
00502 SDL_Surface *optimize(SDL_Surface *src)
00503 {
00504 bool have_transparent = false;
00505 bool have_semi_trans = false;
00506 bool have_opaque = false;
00507
00508 if(!src->format->Amask)
00509 return SDL_DisplayFormat(src);
00510
00511 if(SDL_MUSTLOCK(src))
00512 SDL_LockSurface(src);
00513
00514
00515 for(int y = 0; y < src->h; y++) {
00516 for(int x = 0; x < src->w; x++) {
00517 Uint8 *pixel = (Uint8 *) src->pixels
00518 + (y * src->pitch) + (x * src->format->BytesPerPixel);
00519 Uint32 mapped = get_pixel_mapping (src, pixel);
00520 Uint8 red, green, blue, alpha;
00521 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
00522
00523 if (alpha < 16)
00524 have_transparent = true;
00525 else if (alpha > 240)
00526 have_opaque = true;
00527 else
00528 have_semi_trans = true;
00529 }
00530 }
00531
00532 if(SDL_MUSTLOCK(src))
00533 SDL_UnlockSurface(src);
00534
00535 if (have_semi_trans)
00536 return SDL_DisplayFormatAlpha(src);
00537
00538 if (!have_transparent )
00539 return SDL_DisplayFormat(src);
00540
00541
00542
00543
00544 if (!have_opaque )
00545 return SDL_DisplayFormatAlpha(src);
00546
00547
00548
00549
00550
00551
00552
00553
00554 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA),
00555 src->w, src->h, src->format->BitsPerPixel,
00556 src->format->Rmask, src->format->Gmask, src->format->Bmask, 0);
00557
00558
00559
00560
00561 Uint32 color_key = get_unused_color (src);
00562
00563 if(SDL_MUSTLOCK(src))
00564 SDL_LockSurface(src);
00565 if(SDL_MUSTLOCK(dst))
00566 SDL_LockSurface(dst);
00567
00568
00569 for(int y = 0; y < src->h; y++) {
00570 for(int x = 0; x < src->w; x++) {
00571 Uint8 *src_pixel = (Uint8 *) src->pixels
00572 + (y * src->pitch) + (x * src->format->BytesPerPixel);
00573 Uint8 *dst_pixel = (Uint8 *) dst->pixels
00574 + (y * dst->pitch) + (x * dst->format->BytesPerPixel);
00575 Uint32 mapped = get_pixel_mapping (src, src_pixel);
00576 Uint8 red, green, blue, alpha;
00577 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
00578
00579
00580
00581
00582 if (alpha < 128)
00583 mapped = color_key;
00584
00585 switch (dst->format->BytesPerPixel)
00586 {
00587 case 1:
00588 *dst_pixel = (Uint8) mapped;
00589 break;
00590
00591 case 2:
00592 *((Uint16 *) dst_pixel) = (Uint16) mapped;
00593 break;
00594
00595 case 3:
00596 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00597 dst_pixel[0] = (mapped >> 16) & 0xff;
00598 dst_pixel[1] = (mapped >> 8) & 0xff;
00599 dst_pixel[2] = (mapped >> 0) & 0xff;
00600 #else
00601 dst_pixel[0] = (mapped >> 0) & 0xff;
00602 dst_pixel[1] = (mapped >> 8) & 0xff;
00603 dst_pixel[2] = (mapped >> 16) & 0xff;
00604 #endif
00605 break;
00606
00607 case 4:
00608 *((Uint32 *) dst_pixel) = mapped;
00609 }
00610 }
00611 }
00612
00613 if(SDL_MUSTLOCK(src))
00614 SDL_UnlockSurface(src);
00615 if(SDL_MUSTLOCK(dst))
00616 SDL_UnlockSurface(dst);
00617
00618
00619 SDL_SetColorKey (dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, color_key);
00620 SDL_Surface *convert = SDL_DisplayFormat(dst);
00621 SDL_FreeSurface(dst);
00622 return convert;
00623 }
00624
00625 }
00626
00627 SDLTexture::SDLTexture(SDL_Surface* image) :
00628 texture()
00629 {
00630 texture = optimize(image);
00631
00632
00633 int numerator = 1;
00634 int denominator = 1;
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649 cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
00650 }
00651
00652 SDLTexture::~SDLTexture()
00653 {
00654 SDL_FreeSurface(texture);
00655 }
00656
00657 SDL_Surface*
00658 SDLTexture::get_transform(const Color &color, DrawingEffect effect)
00659 {
00660 if(cache[NO_EFFECT][color] == 0) {
00661 assert(cache[NO_EFFECT][Color::WHITE]);
00662 cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
00663 }
00664 if(cache[effect][color] == 0) {
00665 assert(cache[NO_EFFECT][color]);
00666 switch(effect) {
00667 case NO_EFFECT:
00668 break;
00669 case HORIZONTAL_FLIP:
00670 cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
00671 break;
00672 case VERTICAL_FLIP:
00673 cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
00674 break;
00675 default:
00676 return 0;
00677 }
00678 }
00679 return cache[effect][color];
00680 }
00681
00682
00683