2eb5de6cb5512399b27ae604825de5bd6adfd15e
[supertux.git] / src / video / sdl_texture.cpp
1 //  $Id: sdl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include "sdl_texture.hpp"
23 #include "color.hpp"
24 #include "gameconfig.hpp"
25 #include "main.hpp"
26
27 #include <assert.h>
28
29 #include <SDL.h>
30
31 namespace
32 {
33 #define BILINEAR
34
35 #ifdef NAIVE
36   SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
37   {
38     if(numerator == denominator)
39     {
40       src->refcount++;
41       return src;
42     }
43     else
44     {
45       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);
46       int bpp = dst->format->BytesPerPixel;
47       for(int y = 0;y < dst->h;y++) {
48         for(int x = 0;x < dst->w;x++) {
49           Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
50           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
51           switch(bpp) {
52             case 4:
53               dstpixel[3] = srcpixel[3];
54             case 3:
55               dstpixel[2] = srcpixel[2];
56             case 2:
57               dstpixel[1] = srcpixel[1];
58             case 1:
59               dstpixel[0] = srcpixel[0];
60           }
61         }
62       }
63       return dst;
64     }
65   }
66 #endif
67
68 #ifdef BILINEAR
69   void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
70   {
71     int bpp = src->format->BytesPerPixel;
72     Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
73     Uint32 mapped = 0;
74     switch(bpp) {
75       case 1:
76         mapped = *srcpixel;
77         break;
78       case 2:
79         mapped = *(Uint16 *)srcpixel;
80         break;
81       case 3:
82 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
83         mapped |= srcpixel[0] << 16;
84         mapped |= srcpixel[1] << 8;
85         mapped |= srcpixel[2] << 0;
86 #else
87         mapped |= srcpixel[0] << 0;
88         mapped |= srcpixel[1] << 8;
89         mapped |= srcpixel[2] << 16;
90 #endif
91         break;
92       case 4:
93         mapped = *(Uint32 *)srcpixel;
94         break;
95     }
96     SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
97   }
98
99   void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
100   {
101     color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
102     color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
103     color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
104     color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
105   }
106
107   SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
108   {
109     if(numerator == denominator)
110     {
111       src->refcount++;
112       return src;
113     }
114     else
115     {
116       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);
117       int bpp = dst->format->BytesPerPixel;
118       for(int y = 0;y < dst->h;y++) {
119         for(int x = 0;x < dst->w;x++) {
120           int srcx = x * denominator / numerator;
121           int srcy = y * denominator / numerator;
122           Uint8 color00[4], color01[4], color10[4], color11[4];
123           getpixel(src, srcx, srcy, color00);
124           getpixel(src, srcx + 1, srcy, color01);
125           getpixel(src, srcx, srcy + 1, color10);
126           getpixel(src, srcx + 1, srcy + 1, color11);
127           Uint8 color0[4], color1[4], color[4];
128           int remx = x * denominator % numerator;
129           merge(color0, color00, color01, remx, numerator);
130           merge(color1, color10, color11, remx, numerator);
131           int remy = y * denominator % numerator;
132           merge(color, color0, color1, remy, numerator);
133           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
134           Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
135           switch(bpp) {
136             case 1:
137               *dstpixel = mapped;
138               break;
139             case 2:
140               *(Uint16 *)dstpixel = mapped;
141               break;
142             case 3:
143 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
144               dstpixel[0] = (mapped >> 16) & 0xff;
145               dstpixel[1] = (mapped >> 8) & 0xff;
146               dstpixel[2] = (mapped >> 0) & 0xff;
147 #else
148               dstpixel[0] = (mapped >> 0) & 0xff;
149               dstpixel[1] = (mapped >> 8) & 0xff;
150               dstpixel[2] = (mapped >> 16) & 0xff;
151 #endif
152               break;
153             case 4:
154               *(Uint32 *)dstpixel = mapped;
155               break;
156           }
157         }
158       }
159       return dst;
160     }
161   }
162 #endif
163
164   // FIXME: Horizontal and vertical line problem
165 #ifdef BRESENHAM
166   void accumulate(SDL_Surface *src, int srcx, int srcy, int color[4], int weight)
167   {
168     if(srcx < 0 || srcy < 0 || weight == 0) {
169       return;
170     }
171     int bpp = src->format->BytesPerPixel;
172     Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
173     Uint32 mapped = 0;
174     switch(bpp) {
175       case 1:
176         mapped = *srcpixel;
177         break;
178       case 2:
179         mapped = *(Uint16 *)srcpixel;
180         break;
181       case 3:
182 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
183         mapped |= srcpixel[0] << 16;
184         mapped |= srcpixel[1] << 8;
185         mapped |= srcpixel[2] << 0;
186 #else
187         mapped |= srcpixel[0] << 0;
188         mapped |= srcpixel[1] << 8;
189         mapped |= srcpixel[2] << 16;
190 #endif
191         break;
192       case 4:
193         mapped = *(Uint32 *)srcpixel;
194         break;
195     }
196     Uint8 red, green, blue, alpha;
197     SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
198     color[0] += red * weight;
199     color[1] += green * weight;
200     color[2] += blue * weight;
201     color[3] += alpha * weight;
202   }
203
204   void accumulate_line(SDL_Surface *src, int srcy, int line[][4], int linesize, int weight, int numerator, int denominator)
205   {
206     int intpart = denominator / numerator;
207     int fractpart = denominator % numerator;
208     for(int x = 0, xe = 0, srcx = 0;x < linesize;x++) {
209       accumulate(src, srcx, srcy, line[x], (numerator - xe) * weight);
210       srcx++;
211       for(int i = 0;i < intpart - 1;i++) {
212         accumulate(src, srcx, srcy, line[x], numerator * weight);
213         srcx++;
214       }
215       xe += fractpart;
216       if(xe >= numerator) {
217         xe -= numerator;
218         srcx++;
219       }
220       accumulate(src, srcx, srcy, line[x], xe * weight);
221     }
222   }
223
224   SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
225   {
226     if(numerator == denominator)
227     {
228       src->refcount++;
229       return src;
230     }
231     else
232     {
233       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);
234       int bpp = dst->format->BytesPerPixel;
235       int intpart = denominator / numerator;
236       int fractpart = denominator % numerator;
237       for(int y = 0, ye = 0, srcy = 0;y < dst->h;y++) {
238         int line[dst->w][4];
239         memset(line, 0, sizeof(int) * dst->w * 4);
240         accumulate_line(src, srcy, line, dst->w, numerator - ye, numerator, denominator);
241         srcy++;
242         for(int i = 0;i < intpart - 1;i++) {
243           accumulate_line(src, srcy, line, dst->w, numerator, numerator, denominator);
244           srcy++;
245         }
246         ye += fractpart;
247         if(ye >= numerator) {
248           ye -= numerator;
249           srcy++;
250         }
251         accumulate_line(src, srcy, line, dst->w, ye, numerator, denominator);
252         for(int x = 0;x < dst->w;x++) {
253           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
254           Uint32 mapped = SDL_MapRGBA(dst->format, line[x][0] / (denominator * denominator), line[x][1] / (denominator * denominator), line[x][2] / (denominator * denominator), line[x][3] / (denominator * denominator));
255           switch(bpp) {
256             case 1:
257               *dstpixel = mapped;
258               break;
259             case 2:
260               *(Uint16 *)dstpixel = mapped;
261               break;
262             case 3:
263 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
264               dstpixel[0] = (mapped >> 16) & 0xff;
265               dstpixel[1] = (mapped >> 8) & 0xff;
266               dstpixel[2] = (mapped >> 0) & 0xff;
267 #else
268               dstpixel[0] = (mapped >> 0) & 0xff;
269               dstpixel[1] = (mapped >> 8) & 0xff;
270               dstpixel[2] = (mapped >> 16) & 0xff;
271 #endif
272               break;
273             case 4:
274               *(Uint32 *)dstpixel = mapped;
275               break;
276           }
277         }
278       }
279       return dst;
280     }
281   }
282 #endif
283
284   SDL_Surface *horz_flip(SDL_Surface *src)
285   {
286     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);
287     int bpp = dst->format->BytesPerPixel;
288     for(int y = 0;y < dst->h;y++) {
289       for(int x = 0;x < dst->w;x++) {
290         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
291         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
292         switch(bpp) {
293           case 4:
294             dstpixel[3] = srcpixel[3];
295           case 3:
296             dstpixel[2] = srcpixel[2];
297           case 2:
298             dstpixel[1] = srcpixel[1];
299           case 1:
300             dstpixel[0] = srcpixel[0];
301         }
302       }
303     }
304     return dst;
305   }
306
307   SDL_Surface *vert_flip(SDL_Surface *src)
308   {
309     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);
310     int bpp = dst->format->BytesPerPixel;
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 + (dst->h - y - 1) * dst->pitch + x * bpp;
315         switch(bpp) {
316           case 4:
317             dstpixel[3] = srcpixel[3];
318           case 3:
319             dstpixel[2] = srcpixel[2];
320           case 2:
321             dstpixel[1] = srcpixel[1];
322           case 1:
323             dstpixel[0] = srcpixel[0];
324         }
325       }
326     }
327     return dst;
328   }
329
330   SDL_Surface *colorize(SDL_Surface *src, const Color &color)
331   {
332     // FIXME: This is really slow
333     assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
334     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);
335     int bpp = dst->format->BytesPerPixel;
336     for(int y = 0;y < dst->h;y++) {
337       for(int x = 0;x < dst->w;x++) {
338         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
339         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
340         Uint32 mapped = 0;
341         switch(bpp) {
342           case 1:
343             mapped = *srcpixel;
344             break;
345           case 2:
346             mapped = *(Uint16 *)srcpixel;
347             break;
348           case 3:
349 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
350             mapped |= srcpixel[0] << 16;
351             mapped |= srcpixel[1] << 8;
352             mapped |= srcpixel[2] << 0;
353 #else
354             mapped |= srcpixel[0] << 0;
355             mapped |= srcpixel[1] << 8;
356             mapped |= srcpixel[2] << 16;
357 #endif
358             break;
359           case 4:
360             mapped = *(Uint32 *)srcpixel;
361             break;
362         }
363         Uint8 red, green, blue, alpha;
364         SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
365         red = (Uint8) (red * color.red);
366         green = (Uint8) (green * color.green);
367         blue = (Uint8) (blue * color.blue);
368         mapped = SDL_MapRGBA(dst->format, red, green, blue, alpha);
369         switch(bpp) {
370           case 1:
371             *dstpixel = mapped;
372             break;
373           case 2:
374             *(Uint16 *)dstpixel = mapped;
375             break;
376           case 3:
377 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
378             dstpixel[0] = (mapped >> 16) & 0xff;
379             dstpixel[1] = (mapped >> 8) & 0xff;
380             dstpixel[2] = (mapped >> 0) & 0xff;
381 #else
382             dstpixel[0] = (mapped >> 0) & 0xff;
383             dstpixel[1] = (mapped >> 8) & 0xff;
384             dstpixel[2] = (mapped >> 16) & 0xff;
385 #endif
386             break;
387           case 4:
388             *(Uint32 *)dstpixel = mapped;
389             break;
390         }
391       }
392     }
393     return dst;
394   }
395 }
396
397 namespace SDL
398 {
399   Texture::Texture(SDL_Surface* image)
400   {
401     texture = SDL_DisplayFormatAlpha(image);
402     //width = texture->w;
403     //height = texture->h;
404     int numerator, denominator;
405     float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
406     float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
407     if(xfactor < yfactor)
408     {
409       numerator = config->screenwidth;
410       denominator = SCREEN_WIDTH;
411     }
412     else
413     {
414       numerator = config->screenheight;
415       denominator = SCREEN_HEIGHT;
416     }
417     cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
418   }
419
420   Texture::~Texture()
421   {
422     SDL_FreeSurface(texture);
423   }
424
425   SDL_Surface *Texture::get_transform(const Color &color, DrawingEffect effect)
426   {
427     if(cache[NO_EFFECT][color] == 0) {
428       assert(cache[NO_EFFECT][Color::WHITE]);
429       cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
430     }
431     if(cache[effect][color] == 0) {
432       assert(cache[NO_EFFECT][color]);
433       switch(effect) {
434         case NO_EFFECT:
435           break;
436         case HORIZONTAL_FLIP:
437           cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
438           break;
439         case VERTICAL_FLIP:
440           cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
441           break;
442         default:
443           return 0;
444       }
445     }
446     return cache[effect][color];
447   }
448 }