SDL: slight performance boost to lightmaps
[supertux.git] / src / video / sdl_lightmap.cpp
1 //  $Id: sdl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
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 #include <config.h>
20
21 #include <functional>
22 #include <algorithm>
23 #include <cassert>
24 #include <iostream>
25 #include <SDL_image.h>
26 #include <sstream>
27 #include <iomanip>
28 #include <physfs.h>
29
30 #include "glutil.hpp"
31 #include "sdl_lightmap.hpp"
32 #include "sdl_texture.hpp"
33 #include "drawing_context.hpp"
34 #include "drawing_request.hpp"
35 #include "renderer.hpp"
36 #include "surface.hpp"
37 #include "font.hpp"
38 #include "main.hpp"
39 #include "gameconfig.hpp"
40 #include "texture.hpp"
41 #include "texture_manager.hpp"
42 #include "obstack/obstackpp.hpp"
43
44 namespace SDL
45 {
46   Lightmap::Lightmap()
47   {
48     screen = SDL_GetVideoSurface();
49
50     width = screen->w;
51     height = screen->h;
52
53     red_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
54     green_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
55     blue_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
56   }
57
58   Lightmap::~Lightmap()
59   {
60     free(red_channel);
61     free(green_channel);
62     free(blue_channel);
63   }
64
65   void
66   Lightmap::start_draw(const Color &ambient_color)
67   {
68     memset(red_channel, (Uint8) (ambient_color.red * 255), width * height * sizeof(Uint8));
69     memset(green_channel, (Uint8) (ambient_color.green * 255), width * height * sizeof(Uint8));
70     memset(blue_channel, (Uint8) (ambient_color.blue * 255), width * height * sizeof(Uint8));
71   }
72
73   void
74   Lightmap::end_draw()
75   {
76   }
77
78   void
79   Lightmap::do_draw()
80   {
81     // FIXME: This is really slow
82     int bpp = screen->format->BytesPerPixel;
83     Uint8 *pixel = (Uint8 *) screen->pixels;
84     int loc = 0;
85     for(int y = 0;y < height;y++) {
86       for(int x = 0;x < width;x++, pixel += bpp, loc++) {
87         if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
88         {
89           continue;
90         }
91         Uint32 mapped = 0;
92         switch(bpp) {
93           case 1:
94             mapped = *pixel;
95             break;
96           case 2:
97             mapped = *(Uint16 *)pixel;
98             break;
99           case 3:
100 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
101             mapped |= pixel[0] << 16;
102             mapped |= pixel[1] << 8;
103             mapped |= pixel[2] << 0;
104 #else
105             mapped |= pixel[0] << 0;
106             mapped |= pixel[1] << 8;
107             mapped |= pixel[2] << 16;
108 #endif
109             break;
110           case 4:
111             mapped = *(Uint32 *)pixel;
112             break;
113         }
114         Uint8 red, green, blue, alpha;
115         SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
116         red = (red * red_channel[loc]) >> 8;
117         green = (green * green_channel[loc]) >> 8;
118         blue = (blue * blue_channel[loc]) >> 8;
119         mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
120         switch(bpp) {
121           case 1:
122             *pixel = mapped;
123             break;
124           case 2:
125             *(Uint16 *)pixel = mapped;
126             break;
127           case 3:
128 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
129             pixel[0] = (mapped >> 16) & 0xff;
130             pixel[1] = (mapped >> 8) & 0xff;
131             pixel[2] = (mapped >> 0) & 0xff;
132 #else
133             pixel[0] = (mapped >> 0) & 0xff;
134             pixel[1] = (mapped >> 8) & 0xff;
135             pixel[2] = (mapped >> 16) & 0xff;
136 #endif
137             break;
138           case 4:
139             *(Uint32 *)pixel = mapped;
140             break;
141         }
142       }
143       pixel += screen->pitch - width * bpp;
144     }
145   }
146
147   void Lightmap::light_blit(SDL_Surface *src, int dstx, int dsty,
148                             int srcx, int srcy, int blit_width, int blit_height)
149   {
150     int bpp = src->format->BytesPerPixel;
151       Uint8 *pixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
152     int loc = dsty * width + dstx;
153     for(int y = 0;y < blit_height;y++) {
154       for(int x = 0;x < blit_width;x++, pixel += bpp, loc++) {
155         if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
156         {
157           continue;
158         }
159         if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
160         {
161           continue;
162         }
163
164         Uint32 mapped = 0;
165         switch(bpp) {
166           case 1:
167             mapped = *pixel;
168             break;
169           case 2:
170             mapped = *(Uint16 *)pixel;
171             break;
172           case 3:
173 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
174             mapped |= pixel[0] << 16;
175             mapped |= pixel[1] << 8;
176             mapped |= pixel[2] << 0;
177 #else
178             mapped |= pixel[0] << 0;
179             mapped |= pixel[1] << 8;
180             mapped |= pixel[2] << 16;
181 #endif
182             break;
183           case 4:
184             mapped = *(Uint32 *)pixel;
185             break;
186         }
187         Uint8 red, green, blue, alpha;
188         SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
189
190         if(red != 0)
191         {
192           int redsum = red_channel[loc] + (red * alpha >> 8);
193           red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
194         }
195         if(green != 0)
196         {
197           int greensum = green_channel[loc] + (green * alpha >> 8);
198           green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
199         }
200         if(blue != 0)
201         {
202           int bluesum = blue_channel[loc] + (blue * alpha >> 8);
203           blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
204         }
205       }
206       pixel += src->pitch - blit_width * bpp;
207       loc += width - blit_width;
208     }
209   }
210
211   void
212   Lightmap::draw_surface(const DrawingRequest& request)
213   {
214     if((request.color.red == 0.0 && request.color.green == 0.0 && request.color.blue == 0.0) || request.color.alpha == 0.0 || request.alpha == 0.0)
215     {
216       return;
217     }
218     //FIXME: support parameters request.alpha, request.angle, request.blend
219  
220     const Surface* surface = (const Surface*) request.request_data;
221     SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
222     DrawingEffect effect = request.drawing_effect;
223     if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
224
225     SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
226
227     // get and check SDL_Surface
228     if (transform == 0) {
229       std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
230       return;
231     }   
232
233     int ox, oy;
234     if (effect == HORIZONTAL_FLIP)
235     {
236       ox = sdltexture->get_texture_width() - surface->get_x() - surface->get_width();
237     }
238     else
239     {
240       ox = surface->get_x();
241     }
242     if (effect == VERTICAL_FLIP)
243     {
244       oy = sdltexture->get_texture_height() - surface->get_y() - surface->get_height();
245     }
246     else
247     {
248       oy = surface->get_y();
249     }
250
251     int numerator, denominator;
252     float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
253     float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
254     if(xfactor < yfactor)
255     {
256       numerator = config->screenwidth;
257       denominator = SCREEN_WIDTH;
258     }
259     else
260     {
261       numerator = config->screenheight;
262       denominator = SCREEN_HEIGHT;
263     }
264
265     int dstx = (int) request.pos.x * numerator / denominator;
266     int dsty = (int) request.pos.y * numerator / denominator;
267     int srcx = ox * numerator / denominator;
268     int srcy = oy * numerator / denominator;
269     int blit_width = surface->get_width() * numerator / denominator;
270     int blit_height = surface->get_height() * numerator / denominator;
271     light_blit(transform, dstx, dsty, srcx, srcy, blit_width, blit_height);
272   }
273
274   void
275   Lightmap::draw_surface_part(const DrawingRequest& request)
276   {
277     const SurfacePartRequest* surfacepartrequest
278       = (SurfacePartRequest*) request.request_data;
279
280     const Surface* surface = surfacepartrequest->surface;
281     SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
282     DrawingEffect effect = request.drawing_effect;
283     if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
284
285     SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
286
287     // get and check SDL_Surface
288     if (transform == 0) {
289       std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
290       return;
291     }   
292
293     int ox, oy;
294     if (effect == HORIZONTAL_FLIP)
295     {
296       ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
297     }
298     else
299     {
300       ox = surface->get_x();
301     }
302     if (effect == VERTICAL_FLIP)
303     {
304       oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
305     }
306     else
307     {
308       oy = surface->get_y();
309     }
310
311     int numerator, denominator;
312     float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
313     float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
314     if(xfactor < yfactor)
315     {
316       numerator = config->screenwidth;
317       denominator = SCREEN_WIDTH;
318     }
319     else
320     {
321       numerator = config->screenheight;
322       denominator = SCREEN_HEIGHT;
323     }
324
325     int dstx = (int) request.pos.x * numerator / denominator;
326     int dsty = (int) request.pos.y * numerator / denominator;
327     int srcx = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
328     int srcy = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
329     int blit_width = (int) surfacepartrequest->size.x * numerator / denominator;
330     int blit_height = (int) surfacepartrequest->size.y * numerator / denominator;
331     light_blit(transform, dstx, dsty, srcx, srcy, blit_width, blit_height);
332   }
333
334   void
335   Lightmap::draw_gradient(const DrawingRequest& request)
336   {
337     const GradientRequest* gradientrequest 
338       = (GradientRequest*) request.request_data;
339     const Color& top = gradientrequest->top;
340     const Color& bottom = gradientrequest->bottom;
341
342     for(int y = 0;y < height;++y)
343     {
344       Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
345       Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
346       Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
347       // FIXME
348       //Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
349       for(int x = 0;x < width;x++) {
350         int loc = y * width + x;
351         red_channel[loc] = std::min(red_channel[loc] + r, 255);
352         green_channel[loc] = std::min(green_channel[loc] + g, 255);
353         blue_channel[loc] = std::min(blue_channel[loc] + b, 255);
354       }
355     }
356   }
357
358   void
359   Lightmap::draw_text(const DrawingRequest& /*request*/)
360   {
361     //const TextRequest* textrequest = (TextRequest*) request.request_data;
362
363     //textrequest->font->draw(textrequest->text, request.pos,
364     //    textrequest->alignment, request.drawing_effect, request.alpha);
365   }
366
367   void
368   Lightmap::draw_filled_rect(const DrawingRequest& request)
369   {
370     const FillRectRequest* fillrectrequest
371       = (FillRectRequest*) request.request_data;
372
373     int rect_x = (int) (request.pos.x * width / SCREEN_WIDTH);
374     int rect_y = (int) (request.pos.y * height / SCREEN_HEIGHT);
375     int rect_w = (int) (fillrectrequest->size.x * width / SCREEN_WIDTH);
376     int rect_h = (int) (fillrectrequest->size.y * height / SCREEN_HEIGHT);
377     Uint8 red = (Uint8) (fillrectrequest->color.red * fillrectrequest->color.alpha * 255);
378     Uint8 green = (Uint8) (fillrectrequest->color.green * fillrectrequest->color.alpha * 255);
379     Uint8 blue = (Uint8) (fillrectrequest->color.blue * fillrectrequest->color.alpha * 255);
380     if(red == 0 && green == 0 && blue == 0)
381     {
382       return;
383     }
384     for(int y = rect_y;y < rect_y + rect_h;y++) {
385       for(int x = rect_x;x < rect_x + rect_w;x++) {
386         int loc = y * width + x;
387         red_channel[loc] = std::min(red_channel[loc] + red, 255);
388         green_channel[loc] = std::min(green_channel[loc] + green, 255);
389         blue_channel[loc] = std::min(blue_channel[loc] + blue, 255);
390       }
391     }
392   }
393
394   void
395   Lightmap::get_light(const DrawingRequest& request) const
396   {
397     const GetLightRequest* getlightrequest 
398       = (GetLightRequest*) request.request_data;
399
400     int x = (int) (request.pos.x * width / SCREEN_WIDTH);
401     int y = (int) (request.pos.y * height / SCREEN_HEIGHT);
402     int loc = y * width + x;
403     *(getlightrequest->color_ptr) = Color(((float)red_channel[loc])/255, ((float)green_channel[loc])/255, ((float)blue_channel[loc])/255);
404   }
405 }