SDL: Implement lightmaps smaller than the screen size. LIGHTMAP_DIV is calculated...
[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     if(srcx == src->w)
73     {
74       srcx--;
75     }
76     if(srcy == src->h)
77     {
78       srcy--;
79     }
80     Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
81     Uint32 mapped = 0;
82     switch(bpp) {
83       case 1:
84         mapped = *srcpixel;
85         break;
86       case 2:
87         mapped = *(Uint16 *)srcpixel;
88         break;
89       case 3:
90 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
91         mapped |= srcpixel[0] << 16;
92         mapped |= srcpixel[1] << 8;
93         mapped |= srcpixel[2] << 0;
94 #else
95         mapped |= srcpixel[0] << 0;
96         mapped |= srcpixel[1] << 8;
97         mapped |= srcpixel[2] << 16;
98 #endif
99         break;
100       case 4:
101         mapped = *(Uint32 *)srcpixel;
102         break;
103     }
104     SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
105   }
106
107   void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
108   {
109     color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
110     color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
111     color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
112     color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
113   }
114
115   SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
116   {
117     if(numerator == denominator)
118     {
119       src->refcount++;
120       return src;
121     }
122     else
123     {
124       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);
125       int bpp = dst->format->BytesPerPixel;
126       for(int y = 0;y < dst->h;y++) {
127         for(int x = 0;x < dst->w;x++) {
128           int srcx = x * denominator / numerator;
129           int srcy = y * denominator / numerator;
130           Uint8 color00[4], color01[4], color10[4], color11[4];
131           getpixel(src, srcx, srcy, color00);
132           getpixel(src, srcx + 1, srcy, color01);
133           getpixel(src, srcx, srcy + 1, color10);
134           getpixel(src, srcx + 1, srcy + 1, color11);
135           Uint8 color0[4], color1[4], color[4];
136           int remx = x * denominator % numerator;
137           merge(color0, color00, color01, remx, numerator);
138           merge(color1, color10, color11, remx, numerator);
139           int remy = y * denominator % numerator;
140           merge(color, color0, color1, remy, numerator);
141           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
142           Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
143           switch(bpp) {
144             case 1:
145               *dstpixel = mapped;
146               break;
147             case 2:
148               *(Uint16 *)dstpixel = mapped;
149               break;
150             case 3:
151 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
152               dstpixel[0] = (mapped >> 16) & 0xff;
153               dstpixel[1] = (mapped >> 8) & 0xff;
154               dstpixel[2] = (mapped >> 0) & 0xff;
155 #else
156               dstpixel[0] = (mapped >> 0) & 0xff;
157               dstpixel[1] = (mapped >> 8) & 0xff;
158               dstpixel[2] = (mapped >> 16) & 0xff;
159 #endif
160               break;
161             case 4:
162               *(Uint32 *)dstpixel = mapped;
163               break;
164           }
165         }
166       }
167       return dst;
168     }
169   }
170 #endif
171
172   // FIXME: Horizontal and vertical line problem
173 #ifdef BRESENHAM
174   void accumulate(SDL_Surface *src, int srcx, int srcy, int color[4], int weight)
175   {
176     if(srcx < 0 || srcy < 0 || weight == 0) {
177       return;
178     }
179     int bpp = src->format->BytesPerPixel;
180     Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
181     Uint32 mapped = 0;
182     switch(bpp) {
183       case 1:
184         mapped = *srcpixel;
185         break;
186       case 2:
187         mapped = *(Uint16 *)srcpixel;
188         break;
189       case 3:
190 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
191         mapped |= srcpixel[0] << 16;
192         mapped |= srcpixel[1] << 8;
193         mapped |= srcpixel[2] << 0;
194 #else
195         mapped |= srcpixel[0] << 0;
196         mapped |= srcpixel[1] << 8;
197         mapped |= srcpixel[2] << 16;
198 #endif
199         break;
200       case 4:
201         mapped = *(Uint32 *)srcpixel;
202         break;
203     }
204     Uint8 red, green, blue, alpha;
205     SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
206     color[0] += red * weight;
207     color[1] += green * weight;
208     color[2] += blue * weight;
209     color[3] += alpha * weight;
210   }
211
212   void accumulate_line(SDL_Surface *src, int srcy, int line[][4], int linesize, int weight, int numerator, int denominator)
213   {
214     int intpart = denominator / numerator;
215     int fractpart = denominator % numerator;
216     for(int x = 0, xe = 0, srcx = 0;x < linesize;x++) {
217       accumulate(src, srcx, srcy, line[x], (numerator - xe) * weight);
218       srcx++;
219       for(int i = 0;i < intpart - 1;i++) {
220         accumulate(src, srcx, srcy, line[x], numerator * weight);
221         srcx++;
222       }
223       xe += fractpart;
224       if(xe >= numerator) {
225         xe -= numerator;
226         srcx++;
227       }
228       accumulate(src, srcx, srcy, line[x], xe * weight);
229     }
230   }
231
232   SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
233   {
234     if(numerator == denominator)
235     {
236       src->refcount++;
237       return src;
238     }
239     else
240     {
241       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);
242       int bpp = dst->format->BytesPerPixel;
243       int intpart = denominator / numerator;
244       int fractpart = denominator % numerator;
245       for(int y = 0, ye = 0, srcy = 0;y < dst->h;y++) {
246         int line[dst->w][4];
247         memset(line, 0, sizeof(int) * dst->w * 4);
248         accumulate_line(src, srcy, line, dst->w, numerator - ye, numerator, denominator);
249         srcy++;
250         for(int i = 0;i < intpart - 1;i++) {
251           accumulate_line(src, srcy, line, dst->w, numerator, numerator, denominator);
252           srcy++;
253         }
254         ye += fractpart;
255         if(ye >= numerator) {
256           ye -= numerator;
257           srcy++;
258         }
259         accumulate_line(src, srcy, line, dst->w, ye, numerator, denominator);
260         for(int x = 0;x < dst->w;x++) {
261           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
262           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));
263           switch(bpp) {
264             case 1:
265               *dstpixel = mapped;
266               break;
267             case 2:
268               *(Uint16 *)dstpixel = mapped;
269               break;
270             case 3:
271 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
272               dstpixel[0] = (mapped >> 16) & 0xff;
273               dstpixel[1] = (mapped >> 8) & 0xff;
274               dstpixel[2] = (mapped >> 0) & 0xff;
275 #else
276               dstpixel[0] = (mapped >> 0) & 0xff;
277               dstpixel[1] = (mapped >> 8) & 0xff;
278               dstpixel[2] = (mapped >> 16) & 0xff;
279 #endif
280               break;
281             case 4:
282               *(Uint32 *)dstpixel = mapped;
283               break;
284           }
285         }
286       }
287       return dst;
288     }
289   }
290 #endif
291
292   SDL_Surface *horz_flip(SDL_Surface *src)
293   {
294     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);
295     int bpp = dst->format->BytesPerPixel;
296     for(int y = 0;y < dst->h;y++) {
297       for(int x = 0;x < dst->w;x++) {
298         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
299         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
300         switch(bpp) {
301           case 4:
302             dstpixel[3] = srcpixel[3];
303           case 3:
304             dstpixel[2] = srcpixel[2];
305           case 2:
306             dstpixel[1] = srcpixel[1];
307           case 1:
308             dstpixel[0] = srcpixel[0];
309         }
310       }
311     }
312     return dst;
313   }
314
315   SDL_Surface *vert_flip(SDL_Surface *src)
316   {
317     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);
318     int bpp = dst->format->BytesPerPixel;
319     for(int y = 0;y < dst->h;y++) {
320       for(int x = 0;x < dst->w;x++) {
321         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
322         Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
323         switch(bpp) {
324           case 4:
325             dstpixel[3] = srcpixel[3];
326           case 3:
327             dstpixel[2] = srcpixel[2];
328           case 2:
329             dstpixel[1] = srcpixel[1];
330           case 1:
331             dstpixel[0] = srcpixel[0];
332         }
333       }
334     }
335     return dst;
336   }
337
338   SDL_Surface *colorize(SDL_Surface *src, const Color &color)
339   {
340     // FIXME: This is really slow
341     assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
342     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);
343     int bpp = dst->format->BytesPerPixel;
344     for(int y = 0;y < dst->h;y++) {
345       for(int x = 0;x < dst->w;x++) {
346         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
347         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
348         Uint32 mapped = 0;
349         switch(bpp) {
350           case 1:
351             mapped = *srcpixel;
352             break;
353           case 2:
354             mapped = *(Uint16 *)srcpixel;
355             break;
356           case 3:
357 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
358             mapped |= srcpixel[0] << 16;
359             mapped |= srcpixel[1] << 8;
360             mapped |= srcpixel[2] << 0;
361 #else
362             mapped |= srcpixel[0] << 0;
363             mapped |= srcpixel[1] << 8;
364             mapped |= srcpixel[2] << 16;
365 #endif
366             break;
367           case 4:
368             mapped = *(Uint32 *)srcpixel;
369             break;
370         }
371         Uint8 red, green, blue, alpha;
372         SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
373         red = (Uint8) (red * color.red);
374         green = (Uint8) (green * color.green);
375         blue = (Uint8) (blue * color.blue);
376         mapped = SDL_MapRGBA(dst->format, red, green, blue, alpha);
377         switch(bpp) {
378           case 1:
379             *dstpixel = mapped;
380             break;
381           case 2:
382             *(Uint16 *)dstpixel = mapped;
383             break;
384           case 3:
385 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
386             dstpixel[0] = (mapped >> 16) & 0xff;
387             dstpixel[1] = (mapped >> 8) & 0xff;
388             dstpixel[2] = (mapped >> 0) & 0xff;
389 #else
390             dstpixel[0] = (mapped >> 0) & 0xff;
391             dstpixel[1] = (mapped >> 8) & 0xff;
392             dstpixel[2] = (mapped >> 16) & 0xff;
393 #endif
394             break;
395           case 4:
396             *(Uint32 *)dstpixel = mapped;
397             break;
398         }
399       }
400     }
401     return dst;
402   }
403 }
404
405 namespace SDL
406 {
407   Texture::Texture(SDL_Surface* image)
408   {
409     texture = SDL_DisplayFormatAlpha(image);
410     //width = texture->w;
411     //height = texture->h;
412     int numerator, denominator;
413     float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
414     float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
415     if(xfactor < yfactor)
416     {
417       numerator = config->screenwidth;
418       denominator = SCREEN_WIDTH;
419     }
420     else
421     {
422       numerator = config->screenheight;
423       denominator = SCREEN_HEIGHT;
424     }
425     cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
426   }
427
428   Texture::~Texture()
429   {
430     SDL_FreeSurface(texture);
431   }
432
433   SDL_Surface *Texture::get_transform(const Color &color, DrawingEffect effect)
434   {
435     if(cache[NO_EFFECT][color] == 0) {
436       assert(cache[NO_EFFECT][Color::WHITE]);
437       cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
438     }
439     if(cache[effect][color] == 0) {
440       assert(cache[NO_EFFECT][color]);
441       switch(effect) {
442         case NO_EFFECT:
443           break;
444         case HORIZONTAL_FLIP:
445           cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
446           break;
447         case VERTICAL_FLIP:
448           cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
449           break;
450         default:
451           return 0;
452       }
453     }
454     return cache[effect][color];
455   }
456 }