0d19c489fe821b472418b59410fa3fb8f513f64f
[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     for(int y = 0;y < height;y++) {
84       for(int x = 0;x < width;x++) {
85         int loc = y * width + x;
86         if(red_channel[loc] == 255 && green_channel[loc] == 255 && blue_channel[loc] == 255)
87         {
88           continue;
89         }
90         Uint8 *pixel = (Uint8 *) screen->pixels + y * screen->pitch + x * bpp;
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]) / 255;
117         green = (green * green_channel[loc]) / 255;
118         blue = (blue * blue_channel[loc]) / 255;
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     }
144   }
145
146   void Lightmap::light_blit(SDL_Surface *src, int dstx, int dsty,
147                             int srcx, int srcy, int blit_width, int blit_height)
148   {
149     for(int y = 0;y < blit_height;y++) {
150       for(int x = 0;x < blit_width;x++) {
151         if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
152         {
153           continue;
154         }
155         int loc = (y + dsty) * width + (x + dstx);
156         if(red_channel[loc] == 255 && green_channel[loc] == 255 && blue_channel[loc])
157         {
158           continue;
159         }
160
161         Uint8 *pixel = (Uint8 *) src->pixels + (y + srcy) * src->pitch + (x + srcx) * src->format->BytesPerPixel;
162         Uint32 mapped = 0;
163         switch(src->format->BytesPerPixel) {
164           case 1:
165             mapped = *pixel;
166             break;
167           case 2:
168             mapped = *(Uint16 *)pixel;
169             break;
170           case 3:
171 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
172             mapped |= pixel[0] << 16;
173             mapped |= pixel[1] << 8;
174             mapped |= pixel[2] << 0;
175 #else
176             mapped |= pixel[0] << 0;
177             mapped |= pixel[1] << 8;
178             mapped |= pixel[2] << 16;
179 #endif
180             break;
181           case 4:
182             mapped = *(Uint32 *)pixel;
183             break;
184         }
185         Uint8 red, green, blue, alpha;
186         SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
187
188         red_channel[loc] = std::min(red_channel[loc] + (red * alpha / 255), 255);
189         green_channel[loc] = std::min(green_channel[loc] + (green * alpha / 255), 255);
190         blue_channel[loc] = std::min(blue_channel[loc] + (blue * alpha / 255), 255);
191       }
192     }
193   }
194
195   void
196   Lightmap::draw_surface(const DrawingRequest& request)
197   {
198     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)
199     {
200       return;
201     }
202     //FIXME: support parameters request.alpha, request.angle, request.blend
203  
204     const Surface* surface = (const Surface*) request.request_data;
205     SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
206     DrawingEffect effect = request.drawing_effect;
207     if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
208
209     SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
210
211     // get and check SDL_Surface
212     if (transform == 0) {
213       std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
214       return;
215     }   
216
217     int ox, oy;
218     if (effect == HORIZONTAL_FLIP)
219     {
220       ox = sdltexture->get_texture_width() - surface->get_x() - surface->get_width();
221     }
222     else
223     {
224       ox = surface->get_x();
225     }
226     if (effect == VERTICAL_FLIP)
227     {
228       oy = sdltexture->get_texture_height() - surface->get_y() - surface->get_height();
229     }
230     else
231     {
232       oy = surface->get_y();
233     }
234
235     int numerator, denominator;
236     float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
237     float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
238     if(xfactor < yfactor)
239     {
240       numerator = config->screenwidth;
241       denominator = SCREEN_WIDTH;
242     }
243     else
244     {
245       numerator = config->screenheight;
246       denominator = SCREEN_HEIGHT;
247     }
248
249     int dstx = (int) request.pos.x * numerator / denominator;
250     int dsty = (int) request.pos.y * numerator / denominator;
251     int srcx = ox * numerator / denominator;
252     int srcy = oy * numerator / denominator;
253     int blit_width = surface->get_width() * numerator / denominator;
254     int blit_height = surface->get_height() * numerator / denominator;
255     light_blit(transform, dstx, dsty, srcx, srcy, blit_width, blit_height);
256   }
257
258   void
259   Lightmap::draw_surface_part(const DrawingRequest& request)
260   {
261     const SurfacePartRequest* surfacepartrequest
262       = (SurfacePartRequest*) request.request_data;
263
264     const Surface* surface = surfacepartrequest->surface;
265     SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
266     DrawingEffect effect = request.drawing_effect;
267     if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
268
269     SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
270
271     // get and check SDL_Surface
272     if (transform == 0) {
273       std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
274       return;
275     }   
276
277     int ox, oy;
278     if (effect == HORIZONTAL_FLIP)
279     {
280       ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
281     }
282     else
283     {
284       ox = surface->get_x();
285     }
286     if (effect == VERTICAL_FLIP)
287     {
288       oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
289     }
290     else
291     {
292       oy = surface->get_y();
293     }
294
295     int numerator, denominator;
296     float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
297     float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
298     if(xfactor < yfactor)
299     {
300       numerator = config->screenwidth;
301       denominator = SCREEN_WIDTH;
302     }
303     else
304     {
305       numerator = config->screenheight;
306       denominator = SCREEN_HEIGHT;
307     }
308
309     int dstx = (int) request.pos.x * numerator / denominator;
310     int dsty = (int) request.pos.y * numerator / denominator;
311     int srcx = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
312     int srcy = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
313     int blit_width = (int) surfacepartrequest->size.x * numerator / denominator;
314     int blit_height = (int) surfacepartrequest->size.y * numerator / denominator;
315     light_blit(transform, dstx, dsty, srcx, srcy, blit_width, blit_height);
316   }
317
318   void
319   Lightmap::draw_gradient(const DrawingRequest& request)
320   {
321     const GradientRequest* gradientrequest 
322       = (GradientRequest*) request.request_data;
323     const Color& top = gradientrequest->top;
324     const Color& bottom = gradientrequest->bottom;
325
326     for(int y = 0;y < height;++y)
327     {
328       Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
329       Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
330       Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
331       // FIXME
332       //Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
333       for(int x = 0;x < width;x++) {
334         int loc = y * width + x;
335         red_channel[loc] = std::min(red_channel[loc] + r, 255);
336         green_channel[loc] = std::min(green_channel[loc] + g, 255);
337         blue_channel[loc] = std::min(blue_channel[loc] + b, 255);
338       }
339     }
340   }
341
342   void
343   Lightmap::draw_text(const DrawingRequest& /*request*/)
344   {
345     //const TextRequest* textrequest = (TextRequest*) request.request_data;
346
347     //textrequest->font->draw(textrequest->text, request.pos,
348     //    textrequest->alignment, request.drawing_effect, request.alpha);
349   }
350
351   void
352   Lightmap::draw_filled_rect(const DrawingRequest& request)
353   {
354     const FillRectRequest* fillrectrequest
355       = (FillRectRequest*) request.request_data;
356
357     int rect_x = (int) (request.pos.x * width / SCREEN_WIDTH);
358     int rect_y = (int) (request.pos.y * height / SCREEN_HEIGHT);
359     int rect_w = (int) (fillrectrequest->size.x * width / SCREEN_WIDTH);
360     int rect_h = (int) (fillrectrequest->size.y * height / SCREEN_HEIGHT);
361     Uint8 red = (Uint8) (fillrectrequest->color.red * fillrectrequest->color.alpha * 255);
362     Uint8 green = (Uint8) (fillrectrequest->color.green * fillrectrequest->color.alpha * 255);
363     Uint8 blue = (Uint8) (fillrectrequest->color.blue * fillrectrequest->color.alpha * 255);
364     if(red == 0 && green == 0 && blue == 0)
365     {
366       return;
367     }
368     for(int y = rect_y;y < rect_y + rect_h;y++) {
369       for(int x = rect_x;x < rect_x + rect_w;x++) {
370         int loc = y * width + x;
371         red_channel[loc] = std::min(red_channel[loc] + red, 255);
372         green_channel[loc] = std::min(green_channel[loc] + green, 255);
373         blue_channel[loc] = std::min(blue_channel[loc] + blue, 255);
374       }
375     }
376   }
377
378   void
379   Lightmap::get_light(const DrawingRequest& request) const
380   {
381     const GetLightRequest* getlightrequest 
382       = (GetLightRequest*) request.request_data;
383
384     int x = (int) (request.pos.x * width / SCREEN_WIDTH);
385     int y = (int) (request.pos.y * height / SCREEN_HEIGHT);
386     int loc = y * width + x;
387     *(getlightrequest->color_ptr) = Color(((float)red_channel[loc])/255, ((float)green_channel[loc])/255, ((float)blue_channel[loc])/255);
388   }
389 }