make SDL texture optimization less aggressive and generally stupider
[supertux.git] / src / video / sdl / sdl_texture.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
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.
8 //
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.
13 //
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/>.
16
17 #include <config.h>
18
19 #include "supertux/gameconfig.hpp"
20 #include "supertux/globals.hpp"
21 #include "video/color.hpp"
22 #include "video/sdl/sdl_texture.hpp"
23
24 #include <assert.h>
25
26 #include <SDL.h>
27
28 namespace {
29 #define BILINEAR
30
31 #ifdef NAIVE
32 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
33 {
34   if(numerator == denominator)
35   {
36     src->refcount++;
37     return src;
38   }
39   else
40   {
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;
43     if(SDL_MUSTLOCK(src))
44     {
45       SDL_LockSurface(src);
46     }
47     if(SDL_MUSTLOCK(dst))
48     {
49       SDL_LockSurface(dst);
50     }
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;
55         switch(bpp) {
56           case 4:
57             dstpixel[3] = srcpixel[3];
58           case 3:
59             dstpixel[2] = srcpixel[2];
60           case 2:
61             dstpixel[1] = srcpixel[1];
62           case 1:
63             dstpixel[0] = srcpixel[0];
64         }
65       }
66     }
67     if(SDL_MUSTLOCK(dst))
68     {
69       SDL_UnlockSurface(dst);
70     }
71     if(SDL_MUSTLOCK(src))
72     {
73       SDL_UnlockSurface(src);
74     }
75     if(!src->format->Amask)
76     {
77       if(src->flags & SDL_SRCALPHA)
78       {
79         SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
80       }
81       if(src->flags & SDL_SRCCOLORKEY)
82       {
83         SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
84       }
85     }
86     return dst;
87   }
88 } // namespace
89 #endif
90
91 #ifdef BILINEAR
92 void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
93 {
94   int bpp = src->format->BytesPerPixel;
95   if(srcx == src->w)
96   {
97     srcx--;
98   }
99   if(srcy == src->h)
100   {
101     srcy--;
102   }
103   Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
104   Uint32 mapped = 0;
105   switch(bpp) {
106     case 1:
107       mapped = *srcpixel;
108       break;
109     case 2:
110       mapped = *(Uint16 *)srcpixel;
111       break;
112     case 3:
113 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
114       mapped |= srcpixel[0] << 16;
115       mapped |= srcpixel[1] << 8;
116       mapped |= srcpixel[2] << 0;
117 #else
118       mapped |= srcpixel[0] << 0;
119       mapped |= srcpixel[1] << 8;
120       mapped |= srcpixel[2] << 16;
121 #endif
122       break;
123     case 4:
124       mapped = *(Uint32 *)srcpixel;
125       break;
126   }
127   SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
128 }
129
130 void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
131 {
132   color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
133   color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
134   color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
135   color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
136 }
137
138 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
139 {
140   if(numerator == denominator)
141   {
142     src->refcount++;
143     return src;
144   }
145   else
146   {
147     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);
148     int bpp = dst->format->BytesPerPixel;
149     if(SDL_MUSTLOCK(src))
150     {
151       SDL_LockSurface(src);
152     }
153     if(SDL_MUSTLOCK(dst))
154     {
155       SDL_LockSurface(dst);
156     }
157     for(int y = 0;y < dst->h;y++) {
158       for(int x = 0;x < dst->w;x++) {
159         int srcx = x * denominator / numerator;
160         int srcy = y * denominator / numerator;
161         Uint8 color00[4], color01[4], color10[4], color11[4];
162         getpixel(src, srcx, srcy, color00);
163         getpixel(src, srcx + 1, srcy, color01);
164         getpixel(src, srcx, srcy + 1, color10);
165         getpixel(src, srcx + 1, srcy + 1, color11);
166         Uint8 color0[4], color1[4], color[4];
167         int remx = x * denominator % numerator;
168         merge(color0, color00, color01, remx, numerator);
169         merge(color1, color10, color11, remx, numerator);
170         int remy = y * denominator % numerator;
171         merge(color, color0, color1, remy, numerator);
172         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
173         Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
174         switch(bpp) {
175           case 1:
176             *dstpixel = mapped;
177             break;
178           case 2:
179             *(Uint16 *)dstpixel = mapped;
180             break;
181           case 3:
182 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
183             dstpixel[0] = (mapped >> 16) & 0xff;
184             dstpixel[1] = (mapped >> 8) & 0xff;
185             dstpixel[2] = (mapped >> 0) & 0xff;
186 #else
187             dstpixel[0] = (mapped >> 0) & 0xff;
188             dstpixel[1] = (mapped >> 8) & 0xff;
189             dstpixel[2] = (mapped >> 16) & 0xff;
190 #endif
191             break;
192           case 4:
193             *(Uint32 *)dstpixel = mapped;
194             break;
195         }
196       }
197     }
198     if(SDL_MUSTLOCK(dst))
199     {
200       SDL_UnlockSurface(dst);
201     }
202     if(SDL_MUSTLOCK(src))
203     {
204       SDL_UnlockSurface(src);
205     }
206     if(!src->format->Amask)
207     {
208       if(src->flags & SDL_SRCALPHA)
209       {
210         SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
211       }
212       if(src->flags & SDL_SRCCOLORKEY)
213       {
214         SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
215       }
216     }
217     return dst;
218   }
219 }
220 #endif
221
222 SDL_Surface *horz_flip(SDL_Surface *src)
223 {
224   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);
225   int bpp = dst->format->BytesPerPixel;
226   if(SDL_MUSTLOCK(src))
227   {
228     SDL_LockSurface(src);
229   }
230   if(SDL_MUSTLOCK(dst))
231   {
232     SDL_LockSurface(dst);
233   }
234   for(int y = 0;y < dst->h;y++) {
235     for(int x = 0;x < dst->w;x++) {
236       Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
237       Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
238       switch(bpp) {
239         case 4:
240           dstpixel[3] = srcpixel[3];
241         case 3:
242           dstpixel[2] = srcpixel[2];
243         case 2:
244           dstpixel[1] = srcpixel[1];
245         case 1:
246           dstpixel[0] = srcpixel[0];
247       }
248     }
249   }
250   if(SDL_MUSTLOCK(dst))
251   {
252     SDL_UnlockSurface(dst);
253   }
254   if(SDL_MUSTLOCK(src))
255   {
256     SDL_UnlockSurface(src);
257   }
258   if(!src->format->Amask)
259   {
260     if(src->flags & SDL_SRCALPHA)
261     {
262       SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
263     }
264     if(src->flags & SDL_SRCCOLORKEY)
265     {
266       SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
267     }
268   }
269   return dst;
270 }
271
272 SDL_Surface *vert_flip(SDL_Surface *src)
273 {
274   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);
275   int bpp = dst->format->BytesPerPixel;
276   if(SDL_MUSTLOCK(src))
277   {
278     SDL_LockSurface(src);
279   }
280   if(SDL_MUSTLOCK(dst))
281   {
282     SDL_LockSurface(dst);
283   }
284   for(int y = 0;y < dst->h;y++) {
285     for(int x = 0;x < dst->w;x++) {
286       Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
287       Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
288       switch(bpp) {
289         case 4:
290           dstpixel[3] = srcpixel[3];
291         case 3:
292           dstpixel[2] = srcpixel[2];
293         case 2:
294           dstpixel[1] = srcpixel[1];
295         case 1:
296           dstpixel[0] = srcpixel[0];
297       }
298     }
299   }
300   if(SDL_MUSTLOCK(dst))
301   {
302     SDL_UnlockSurface(dst);
303   }
304   if(SDL_MUSTLOCK(src))
305   {
306     SDL_UnlockSurface(src);
307   }
308   if(!src->format->Amask)
309   {
310     if(src->flags & SDL_SRCALPHA)
311     {
312       SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
313     }
314     if(src->flags & SDL_SRCCOLORKEY)
315     {
316       SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
317     }
318   }
319   return dst;
320 }
321
322 SDL_Surface *colorize(SDL_Surface *src, const Color &color)
323 {
324   // FIXME: This is really slow
325   assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
326   int red = (int) (color.red * 256);
327   int green = (int) (color.green * 256);
328   int blue = (int) (color.blue * 256);
329   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);
330   int bpp = dst->format->BytesPerPixel;
331   if(SDL_MUSTLOCK(src))
332   {
333     SDL_LockSurface(src);
334   }
335   if(SDL_MUSTLOCK(dst))
336   {
337     SDL_LockSurface(dst);
338   }
339   for(int y = 0;y < dst->h;y++) {
340     for(int x = 0;x < dst->w;x++) {
341       Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
342       Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
343       Uint32 mapped = 0;
344       switch(bpp) {
345         case 1:
346           mapped = *srcpixel;
347           break;
348         case 2:
349           mapped = *(Uint16 *)srcpixel;
350           break;
351         case 3:
352 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
353           mapped |= srcpixel[0] << 16;
354           mapped |= srcpixel[1] << 8;
355           mapped |= srcpixel[2] << 0;
356 #else
357           mapped |= srcpixel[0] << 0;
358           mapped |= srcpixel[1] << 8;
359           mapped |= srcpixel[2] << 16;
360 #endif
361           break;
362         case 4:
363           mapped = *(Uint32 *)srcpixel;
364           break;
365       }
366       if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
367       {
368         Uint8 r, g, b, a;
369         SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
370         mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
371       }
372       switch(bpp) {
373         case 1:
374           *dstpixel = mapped;
375           break;
376         case 2:
377           *(Uint16 *)dstpixel = mapped;
378           break;
379         case 3:
380 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
381           dstpixel[0] = (mapped >> 16) & 0xff;
382           dstpixel[1] = (mapped >> 8) & 0xff;
383           dstpixel[2] = (mapped >> 0) & 0xff;
384 #else
385           dstpixel[0] = (mapped >> 0) & 0xff;
386           dstpixel[1] = (mapped >> 8) & 0xff;
387           dstpixel[2] = (mapped >> 16) & 0xff;
388 #endif
389           break;
390         case 4:
391           *(Uint32 *)dstpixel = mapped;
392           break;
393       }
394     }
395   }
396   if(SDL_MUSTLOCK(dst))
397   {
398     SDL_UnlockSurface(dst);
399   }
400   if(SDL_MUSTLOCK(src))
401   {
402     SDL_UnlockSurface(src);
403   }
404   if(!src->format->Amask)
405   {
406     if(src->flags & SDL_SRCALPHA)
407     {
408       SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
409     }
410     if(src->flags & SDL_SRCCOLORKEY)
411     {
412       SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
413     }
414   }
415   return dst;
416 }
417
418 SDL_Surface *optimize(SDL_Surface *src)
419 {
420   if(!src->format->Amask)
421   {
422     return SDL_DisplayFormat(src);
423   }
424   else
425   {
426     int bpp;
427     bool colors[(1 << 12)];
428     memset(colors, 0, (1 << 12) * sizeof(bool));
429 #if 0
430     int transparent = 0;
431     int opaque = 0;
432     int semitransparent = 0;
433     int alphasum = 0;
434     int squaredalphasum = 0;
435
436     int bpp = src->format->BytesPerPixel;
437     if(SDL_MUSTLOCK(src))
438     {
439       SDL_LockSurface(src);
440     }
441     for(int y = 0;y < src->h;y++) {
442       for(int x = 0;x < src->w;x++) {
443         Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
444         Uint32 mapped = 0;
445         switch(bpp) {
446           case 1:
447             mapped = *pixel;
448             break;
449           case 2:
450             mapped = *(Uint16 *)pixel;
451             break;
452           case 3:
453 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
454             mapped |= pixel[0] << 16;
455             mapped |= pixel[1] << 8;
456             mapped |= pixel[2] << 0;
457 #else
458             mapped |= pixel[0] << 0;
459             mapped |= pixel[1] << 8;
460             mapped |= pixel[2] << 16;
461 #endif
462             break;
463           case 4:
464             mapped = *(Uint32 *)pixel;
465             break;
466         }
467         Uint8 red, green, blue, alpha;
468         SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
469         if(alpha < 16)
470         {
471           transparent++;
472         }
473         else if (alpha > 240)
474         {
475           opaque++;
476           alphasum += alpha;
477           squaredalphasum += alpha * alpha;
478         }
479         else
480         {
481           semitransparent++;
482           squaredalphasum += alpha * alpha;
483         }
484         if(alpha != 0)
485         {
486           colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] = true;
487         }
488       }
489     }
490     if(SDL_MUSTLOCK(src))
491     {
492       SDL_UnlockSurface(src);
493     }
494     int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
495     int avgsquaredalpha = (opaque + semitransparent) ? squaredalphasum / (opaque + semitransparent) : 0;
496     int alphavariance = avgsquaredalpha - avgalpha * avgalpha;
497     if(semitransparent > ((transparent + opaque + semitransparent) / 8) && alphavariance > 16)
498     {
499       return SDL_DisplayFormatAlpha(src);
500     }
501 #endif
502     int keycolor = -1;
503     for(int i = 0;i < (1 << 12);i++)
504     {
505       if(!colors[i])
506       {
507         keycolor = i;
508       }
509     }
510     if(keycolor == -1)
511     {
512       return SDL_DisplayFormatAlpha(src);
513     }
514     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);
515     bpp = dst->format->BytesPerPixel;
516     Uint32 key = SDL_MapRGB(dst->format, (((keycolor & 0xf00) >> 4) | 0xf), ((keycolor & 0xf0) | 0xf), (((keycolor & 0xf) << 4) | 0xf));
517     if(SDL_MUSTLOCK(src))
518     {
519       SDL_LockSurface(src);
520     }
521     if(SDL_MUSTLOCK(dst))
522     {
523       SDL_LockSurface(dst);
524     }
525     for(int y = 0;y < dst->h;y++) {
526       for(int x = 0;x < dst->w;x++) {
527         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
528         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
529         Uint32 mapped = 0;
530         switch(bpp) {
531           case 1:
532             mapped = *srcpixel;
533             break;
534           case 2:
535             mapped = *(Uint16 *)srcpixel;
536             break;
537           case 3:
538 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
539             mapped |= srcpixel[0] << 16;
540             mapped |= srcpixel[1] << 8;
541             mapped |= srcpixel[2] << 0;
542 #else
543             mapped |= srcpixel[0] << 0;
544             mapped |= srcpixel[1] << 8;
545             mapped |= srcpixel[2] << 16;
546 #endif
547             break;
548           case 4:
549             mapped = *(Uint32 *)srcpixel;
550             break;
551         }
552         Uint8 red, green, blue, alpha;
553         SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
554         //if(alpha < (avgalpha / 4))
555         if(alpha < 8)
556         {
557           mapped = key;
558         }
559         else
560         {
561           mapped = SDL_MapRGB(dst->format, red, green, blue);
562         }
563         switch(bpp) {
564           case 1:
565             *dstpixel = mapped;
566             break;
567           case 2:
568             *(Uint16 *)dstpixel = mapped;
569             break;
570           case 3:
571 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
572             dstpixel[0] = (mapped >> 16) & 0xff;
573             dstpixel[1] = (mapped >> 8) & 0xff;
574             dstpixel[2] = (mapped >> 0) & 0xff;
575 #else
576             dstpixel[0] = (mapped >> 0) & 0xff;
577             dstpixel[1] = (mapped >> 8) & 0xff;
578             dstpixel[2] = (mapped >> 16) & 0xff;
579 #endif
580             break;
581           case 4:
582             *(Uint32 *)dstpixel = mapped;
583             break;
584         }
585       }
586     }
587     if(SDL_MUSTLOCK(dst))
588     {
589       SDL_UnlockSurface(dst);
590     }
591     if(SDL_MUSTLOCK(src))
592     {
593       SDL_UnlockSurface(src);
594     }
595     /*
596     if(avgalpha < 240)
597     {
598       SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, avgalpha);
599     }
600     */
601     SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
602     SDL_Surface *convert = SDL_DisplayFormat(dst);
603     SDL_FreeSurface(dst);
604     return convert;
605   }
606 }
607 }
608
609 SDLTexture::SDLTexture(SDL_Surface* image) :
610   texture()
611 {
612   texture = optimize(image);
613   //width = texture->w;
614   //height = texture->h;
615   int numerator   = 1;
616   int denominator = 1;
617   //FIXME: float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
618   //FIXME: float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
619   /* FIXME: 
620      if(xfactor < yfactor)
621      {
622      numerator = config->screenwidth;
623      denominator = SCREEN_WIDTH;
624      }
625      else
626      {
627      numerator = config->screenheight;
628      denominator = SCREEN_HEIGHT;
629      }
630   */
631   cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
632 }
633
634 SDLTexture::~SDLTexture()
635 {
636   SDL_FreeSurface(texture);
637 }
638
639 SDL_Surface*
640 SDLTexture::get_transform(const Color &color, DrawingEffect effect)
641 {
642   if(cache[NO_EFFECT][color] == 0) {
643     assert(cache[NO_EFFECT][Color::WHITE]);
644     cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
645   }
646   if(cache[effect][color] == 0) {
647     assert(cache[NO_EFFECT][color]);
648     switch(effect) {
649       case NO_EFFECT:
650         break;
651       case HORIZONTAL_FLIP:
652         cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
653         break;
654       case VERTICAL_FLIP:
655         cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
656         break;
657       default:
658         return 0;
659     }
660   }
661   return cache[effect][color];
662 }
663
664 /* EOF */