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