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