Merged changes from branches/supertux-milestone2-grumbel/ to trunk/supertux/
[supertux.git] / src / video / sdl / sdl_lightmap.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include <iostream>
18
19 #include "video/sdl/sdl_lightmap.hpp"
20 #include "video/sdl/sdl_surface_data.hpp"
21 #include "video/sdl/sdl_texture.hpp"
22
23 SDLLightmap::SDLLightmap()
24 {
25   screen = SDL_GetVideoSurface();
26
27   //float xfactor = 1.0f; // FIXME: (float) config->screenwidth / SCREEN_WIDTH;
28   //float yfactor = 1.0f; // FIXME: (float) config->screenheight / SCREEN_HEIGHT;
29
30   numerator = 1;
31   denominator = 1;
32
33   /* FIXME:
34      if(xfactor < yfactor)
35      {
36      numerator = config->screenwidth;
37      denominator = SCREEN_WIDTH;
38      }
39      else
40      {
41      numerator = config->screenheight;
42      denominator = SCREEN_HEIGHT;
43      }
44   */
45
46   LIGHTMAP_DIV = 8 * numerator / denominator;
47
48   width = screen->w / LIGHTMAP_DIV;
49   height = screen->h / LIGHTMAP_DIV;
50
51   red_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
52   green_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
53   blue_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
54 }
55
56 SDLLightmap::~SDLLightmap()
57 {
58   free(red_channel);
59   free(green_channel);
60   free(blue_channel);
61 }
62
63 void
64 SDLLightmap::start_draw(const Color &ambient_color)
65 {
66   memset(red_channel, (Uint8) (ambient_color.red * 255), width * height * sizeof(Uint8));
67   memset(green_channel, (Uint8) (ambient_color.green * 255), width * height * sizeof(Uint8));
68   memset(blue_channel, (Uint8) (ambient_color.blue * 255), width * height * sizeof(Uint8));
69 }
70
71 void
72 SDLLightmap::end_draw()
73 {
74 }
75
76 //#define BILINEAR
77
78 #ifdef BILINEAR
79 namespace {
80
81 void merge(Uint8 color[3], Uint8 color0[3], Uint8 color1[3], int rem, int total)
82 {
83   color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
84   color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
85   color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
86 }
87
88 } // namespace
89 #endif
90
91 void
92 SDLLightmap::do_draw()
93 {
94   // FIXME: This is really slow
95   if(LIGHTMAP_DIV == 1)
96   {
97     int bpp = screen->format->BytesPerPixel;
98     if(SDL_MUSTLOCK(screen))
99     {
100       SDL_LockSurface(screen);
101     }
102     Uint8 *pixel = (Uint8 *) screen->pixels;
103     int loc = 0;
104     for(int y = 0;y < height;y++) {
105       for(int x = 0;x < width;x++, pixel += bpp, loc++) {
106         if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
107         {
108           continue;
109         }
110         Uint32 mapped = 0;
111         switch(bpp) {
112           case 1:
113             mapped = *pixel;
114             break;
115           case 2:
116             mapped = *(Uint16 *)pixel;
117             break;
118           case 3:
119 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
120             mapped |= pixel[0] << 16;
121             mapped |= pixel[1] << 8;
122             mapped |= pixel[2] << 0;
123 #else
124             mapped |= pixel[0] << 0;
125             mapped |= pixel[1] << 8;
126             mapped |= pixel[2] << 16;
127 #endif
128             break;
129           case 4:
130             mapped = *(Uint32 *)pixel;
131             break;
132         }
133         Uint8 red, green, blue, alpha;
134         SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
135         red = (red * red_channel[loc]) >> 8;
136         green = (green * green_channel[loc]) >> 8;
137         blue = (blue * blue_channel[loc]) >> 8;
138         mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
139         switch(bpp) {
140           case 1:
141             *pixel = mapped;
142             break;
143           case 2:
144             *(Uint16 *)pixel = mapped;
145             break;
146           case 3:
147 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
148             pixel[0] = (mapped >> 16) & 0xff;
149             pixel[1] = (mapped >> 8) & 0xff;
150             pixel[2] = (mapped >> 0) & 0xff;
151 #else
152             pixel[0] = (mapped >> 0) & 0xff;
153             pixel[1] = (mapped >> 8) & 0xff;
154             pixel[2] = (mapped >> 16) & 0xff;
155 #endif
156             break;
157           case 4:
158             *(Uint32 *)pixel = mapped;
159             break;
160         }
161       }
162       pixel += screen->pitch - width * bpp;
163     }
164     if(SDL_MUSTLOCK(screen))
165     {
166       SDL_UnlockSurface(screen);
167     }
168   }
169   else
170   {
171     int bpp = screen->format->BytesPerPixel;
172     if(SDL_MUSTLOCK(screen))
173     {
174       SDL_LockSurface(screen);
175     }
176     Uint8 *div_pixel = (Uint8 *) screen->pixels;
177     int loc = 0;
178     for(int y = 0;y < height;y++) {
179       for(int x = 0;x < width;x++, div_pixel += bpp * LIGHTMAP_DIV, loc++) {
180         if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
181         {
182           continue;
183         }
184         Uint8 *pixel = div_pixel;
185         for(int div_y = 0;div_y < LIGHTMAP_DIV;div_y++) {
186           for(int div_x = 0;div_x < LIGHTMAP_DIV;pixel += bpp, div_x++) {
187             Uint32 mapped = 0;
188             switch(bpp) {
189               case 1:
190                 mapped = *pixel;
191                 break;
192               case 2:
193                 mapped = *(Uint16 *)pixel;
194                 break;
195               case 3:
196 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
197                 mapped |= pixel[0] << 16;
198                 mapped |= pixel[1] << 8;
199                 mapped |= pixel[2] << 0;
200 #else
201                 mapped |= pixel[0] << 0;
202                 mapped |= pixel[1] << 8;
203                 mapped |= pixel[2] << 16;
204 #endif
205                 break;
206               case 4:
207                 mapped = *(Uint32 *)pixel;
208                 break;
209             }
210             Uint8 red, green, blue, alpha;
211             SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
212
213 #ifdef BILINEAR
214             int xinc = (x + 1 != width ? 1 : 0);
215             int yinc = (y + 1 != height ? width : 0);
216             Uint8 color00[3], color01[3], color10[3], color11[3];
217             {
218               color00[0] = red_channel[loc];
219               color00[1] = green_channel[loc];
220               color00[2] = blue_channel[loc];
221             }
222             {
223               color01[0] = red_channel[loc + xinc];
224               color01[1] = green_channel[loc + xinc];
225               color01[2] = blue_channel[loc + xinc];
226             }
227             {
228               color10[0] = red_channel[loc + yinc];
229               color10[1] = green_channel[loc + yinc];
230               color10[2] = blue_channel[loc + yinc];
231             }
232             {
233               color11[0] = red_channel[loc + yinc + xinc];
234               color11[1] = green_channel[loc + yinc + xinc];
235               color11[2] = blue_channel[loc + yinc + xinc];
236             }
237             Uint8 color0[3], color1[3], color[3];
238             merge(color0, color00, color01, div_x, LIGHTMAP_DIV);
239             merge(color1, color10, color11, div_x, LIGHTMAP_DIV);
240             merge(color, color0, color1, div_y, LIGHTMAP_DIV);
241             red = (red * color[0]) >> 8;
242             green = (green * color[1]) >> 8;
243             blue = (blue * color[2]) >> 8;
244 #else
245             red = (red * red_channel[loc]) >> 8;
246             green = (green * green_channel[loc]) >> 8;
247             blue = (blue * blue_channel[loc]) >> 8;
248 #endif
249
250             mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
251             switch(bpp) {
252               case 1:
253                 *pixel = mapped;
254                 break;
255               case 2:
256                 *(Uint16 *)pixel = mapped;
257                 break;
258               case 3:
259 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
260                 pixel[0] = (mapped >> 16) & 0xff;
261                 pixel[1] = (mapped >> 8) & 0xff;
262                 pixel[2] = (mapped >> 0) & 0xff;
263 #else
264                 pixel[0] = (mapped >> 0) & 0xff;
265                 pixel[1] = (mapped >> 8) & 0xff;
266                 pixel[2] = (mapped >> 16) & 0xff;
267 #endif
268                 break;
269               case 4:
270                 *(Uint32 *)pixel = mapped;
271                 break;
272             }
273           }
274           pixel += screen->pitch - LIGHTMAP_DIV * bpp;
275         }
276       }
277       div_pixel += (screen->pitch - width * bpp) * LIGHTMAP_DIV;
278     }
279     if(SDL_MUSTLOCK(screen))
280     {
281       SDL_UnlockSurface(screen);
282     }
283   }
284 }
285
286 void
287 SDLLightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
288 {
289   dstx /= LIGHTMAP_DIV;
290   dsty /= LIGHTMAP_DIV;
291   int srcx = src_rect->x / LIGHTMAP_DIV;
292   int srcy = src_rect->y / LIGHTMAP_DIV;
293   int blit_width = src_rect->w / LIGHTMAP_DIV;
294   int blit_height = src_rect->h / LIGHTMAP_DIV;
295   int bpp = src->format->BytesPerPixel;
296   if(SDL_MUSTLOCK(src))
297   {
298     SDL_LockSurface(src);
299   }
300   Uint8 *pixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
301   int loc = dsty * width + dstx;
302   for(int y = 0;y < blit_height;y++) {
303     for(int x = 0;x < blit_width;x++, pixel += bpp * LIGHTMAP_DIV, loc++) {
304       if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
305       {
306         continue;
307       }
308       if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
309       {
310         continue;
311       }
312
313       Uint32 mapped = 0;
314       switch(bpp) {
315         case 1:
316           mapped = *pixel;
317           break;
318         case 2:
319           mapped = *(Uint16 *)pixel;
320           break;
321         case 3:
322 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
323           mapped |= pixel[0] << 16;
324           mapped |= pixel[1] << 8;
325           mapped |= pixel[2] << 0;
326 #else
327           mapped |= pixel[0] << 0;
328           mapped |= pixel[1] << 8;
329           mapped |= pixel[2] << 16;
330 #endif
331           break;
332         case 4:
333           mapped = *(Uint32 *)pixel;
334           break;
335       }
336       Uint8 red, green, blue, alpha;
337       SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
338
339       if(red != 0)
340       {
341         int redsum = red_channel[loc] + (red * alpha >> 8);
342         red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
343       }
344       if(green != 0)
345       {
346         int greensum = green_channel[loc] + (green * alpha >> 8);
347         green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
348       }
349       if(blue != 0)
350       {
351         int bluesum = blue_channel[loc] + (blue * alpha >> 8);
352         blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
353       }
354     }
355     pixel += (src->pitch - blit_width * bpp) * LIGHTMAP_DIV;
356     loc += width - blit_width;
357   }
358   if(SDL_MUSTLOCK(src))
359   {
360     SDL_UnlockSurface(src);
361   }
362 }
363
364 /*void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
365   {
366   int bpp = src->format->BytesPerPixel;
367   if(SDL_MUSTLOCK(src))
368   {
369   SDL_LockSurface(src);
370   }
371   Uint8 *pixel = (Uint8 *) src->pixels + src_rect->y * src->pitch + src_rect->x * bpp;
372   int loc = dsty * width + dstx;
373   for(int y = 0;y < src_rect->h;y++) {
374   for(int x = 0;x < src_rect->w;x++, pixel += bpp, loc++) {
375   if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
376   {
377   continue;
378   }
379   if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
380   {
381   continue;
382   }
383
384   Uint32 mapped = 0;
385   switch(bpp) {
386   case 1:
387   mapped = *pixel;
388   break;
389   case 2:
390   mapped = *(Uint16 *)pixel;
391   break;
392   case 3:
393   #if SDL_BYTEORDER == SDL_BIG_ENDIAN
394   mapped |= pixel[0] << 16;
395   mapped |= pixel[1] << 8;
396   mapped |= pixel[2] << 0;
397   #else
398   mapped |= pixel[0] << 0;
399   mapped |= pixel[1] << 8;
400   mapped |= pixel[2] << 16;
401   #endif
402   break;
403   case 4:
404   mapped = *(Uint32 *)pixel;
405   break;
406   }
407   Uint8 red, green, blue, alpha;
408   SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
409
410   if(red != 0)
411   {
412   int redsum = red_channel[loc] + (red * alpha >> 8);
413   red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
414   }
415   if(green != 0)
416   {
417   int greensum = green_channel[loc] + (green * alpha >> 8);
418   green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
419   }
420   if(blue != 0)
421   {
422   int bluesum = blue_channel[loc] + (blue * alpha >> 8);
423   blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
424   }
425   }
426   pixel += src->pitch - src_rect->w * bpp;
427   loc += width - src_rect->w;
428   }
429   if(SDL_MUSTLOCK(src))
430   {
431   SDL_UnlockSurface(src);
432   }
433   }*/
434
435 void
436 SDLLightmap::draw_surface(const DrawingRequest& request)
437 {
438   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)
439   {
440     return;
441   }
442   //FIXME: support parameters request.alpha, request.angle, request.blend
443  
444   const Surface* surface = (const Surface*) request.request_data;
445   SDLTexture *sdltexture = dynamic_cast<SDLTexture *>(surface->get_texture());
446   SDLSurfaceData *surface_data = reinterpret_cast<SDLSurfaceData *>(surface->get_surface_data());
447
448   DrawingEffect effect = request.drawing_effect;
449   if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
450
451   SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
452
453   // get and check SDL_Surface
454   if (transform == 0) {
455     std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
456     return;
457   }
458
459   SDL_Rect *src_rect = surface_data->get_src_rect(effect);
460   int dstx = (int) request.pos.x * numerator / denominator;
461   int dsty = (int) request.pos.y * numerator / denominator;
462   light_blit(transform, src_rect, dstx, dsty);
463 }
464
465 void
466 SDLLightmap::draw_surface_part(const DrawingRequest& request)
467 {
468   const SurfacePartRequest* surfacepartrequest
469     = (SurfacePartRequest*) request.request_data;
470
471   const Surface* surface = surfacepartrequest->surface;
472   SDLTexture *sdltexture = dynamic_cast<SDLTexture *>(surface->get_texture());
473
474   DrawingEffect effect = request.drawing_effect;
475   if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
476
477   SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
478
479   // get and check SDL_Surface
480   if (transform == 0) {
481     std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
482     return;
483   }
484
485   int ox, oy;
486   if (effect == HORIZONTAL_FLIP)
487   {
488     ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
489   }
490   else
491   {
492     ox = surface->get_x();
493   }
494   if (effect == VERTICAL_FLIP)
495   {
496     oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
497   }
498   else
499   {
500     oy = surface->get_y();
501   }
502
503   SDL_Rect src_rect;
504   src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
505   src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
506   src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
507   src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
508   int dstx = (int) request.pos.x * numerator / denominator;
509   int dsty = (int) request.pos.y * numerator / denominator;
510   light_blit(transform, &src_rect, dstx, dsty);
511 }
512
513 void
514 SDLLightmap::draw_gradient(const DrawingRequest& request)
515 {
516   const GradientRequest* gradientrequest 
517     = (GradientRequest*) request.request_data;
518   const Color& top = gradientrequest->top;
519   const Color& bottom = gradientrequest->bottom;
520
521   int loc = 0;
522   for(int y = 0;y < height;++y)
523   {
524     Uint8 red = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
525     Uint8 green = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
526     Uint8 blue = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
527     Uint8 alpha = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
528     for(int x = 0;x < width;x++, loc++) {
529       if(red != 0)
530       {
531         int redsum = red_channel[loc] + (red * alpha >> 8);
532         red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
533       }
534       if(green != 0)
535       {
536         int greensum = green_channel[loc] + (green * alpha >> 8);
537         green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
538       }
539       if(blue != 0)
540       {
541         int bluesum = blue_channel[loc] + (blue * alpha >> 8);
542         blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
543       }
544     }
545   }
546 }
547
548 void
549 SDLLightmap::draw_filled_rect(const DrawingRequest& request)
550 {
551   const FillRectRequest* fillrectrequest
552     = (FillRectRequest*) request.request_data;
553
554   int rect_x = (int) (request.pos.x * width / SCREEN_WIDTH);
555   int rect_y = (int) (request.pos.y * height / SCREEN_HEIGHT);
556   int rect_w = (int) (fillrectrequest->size.x * width / SCREEN_WIDTH);
557   int rect_h = (int) (fillrectrequest->size.y * height / SCREEN_HEIGHT);
558   Uint8 red = (Uint8) (fillrectrequest->color.red * fillrectrequest->color.alpha * 255);
559   Uint8 green = (Uint8) (fillrectrequest->color.green * fillrectrequest->color.alpha * 255);
560   Uint8 blue = (Uint8) (fillrectrequest->color.blue * fillrectrequest->color.alpha * 255);
561   if(red == 0 && green == 0 && blue == 0)
562   {
563     return;
564   }
565   for(int y = rect_y;y < rect_y + rect_h;y++) {
566     for(int x = rect_x;x < rect_x + rect_w;x++) {
567       int loc = y * width + x;
568       if(red != 0)
569       {
570         int redsum = red_channel[loc] + red;
571         red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
572       }
573       if(green != 0)
574       {
575         int greensum = green_channel[loc] + green;
576         green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
577       }
578       if(blue != 0)
579       {
580         int bluesum = blue_channel[loc] + blue;
581         blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
582       }
583     }
584   }
585 }
586
587 void
588 SDLLightmap::get_light(const DrawingRequest& request) const
589 {
590   const GetLightRequest* getlightrequest 
591     = (GetLightRequest*) request.request_data;
592
593   int x = (int) (request.pos.x * width / SCREEN_WIDTH);
594   int y = (int) (request.pos.y * height / SCREEN_HEIGHT);
595   int loc = y * width + x;
596   *(getlightrequest->color_ptr) = Color(((float)red_channel[loc])/255, ((float)green_channel[loc])/255, ((float)blue_channel[loc])/255);
597 }
598
599 /* EOF */