New grow and skid sounds from remaxim
[supertux.git] / src / video / sdl_lightmap.cpp
index 52294b9..595da53 100644 (file)
@@ -30,6 +30,7 @@
 #include "glutil.hpp"
 #include "sdl_lightmap.hpp"
 #include "sdl_texture.hpp"
+#include "sdl_surface_data.hpp"
 #include "drawing_context.hpp"
 #include "drawing_request.hpp"
 #include "renderer.hpp"
@@ -47,8 +48,29 @@ namespace SDL
   {
     screen = SDL_GetVideoSurface();
 
-    width = screen->w;
-    height = screen->h;
+    //float xfactor = 1.0f; // FIXME: (float) config->screenwidth / SCREEN_WIDTH;
+    //float yfactor = 1.0f; // FIXME: (float) config->screenheight / SCREEN_HEIGHT;
+
+    numerator = 1;
+    denominator = 1;
+
+    /* FIXME:
+    if(xfactor < yfactor)
+    {
+      numerator = config->screenwidth;
+      denominator = SCREEN_WIDTH;
+    }
+    else
+    {
+      numerator = config->screenheight;
+      denominator = SCREEN_HEIGHT;
+    }
+    */
+
+    LIGHTMAP_DIV = 8 * numerator / denominator;
+
+    width = screen->w / LIGHTMAP_DIV;
+    height = screen->h / LIGHTMAP_DIV;
 
     red_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
     green_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
@@ -75,19 +97,241 @@ namespace SDL
   {
   }
 
+//#define BILINEAR
+
+#ifdef BILINEAR
+  namespace
+  {
+    void merge(Uint8 color[3], Uint8 color0[3], Uint8 color1[3], int rem, int total)
+    {
+      color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
+      color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
+      color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
+    }
+  }
+#endif
+
   void
   Lightmap::do_draw()
   {
     // FIXME: This is really slow
-    int bpp = screen->format->BytesPerPixel;
-    Uint8 *pixel = (Uint8 *) screen->pixels;
-    int loc = 0;
-    for(int y = 0;y < height;y++) {
-      for(int x = 0;x < width;x++, pixel += bpp, loc++) {
+    if(LIGHTMAP_DIV == 1)
+    {
+      int bpp = screen->format->BytesPerPixel;
+      if(SDL_MUSTLOCK(screen))
+      {
+        SDL_LockSurface(screen);
+      }
+      Uint8 *pixel = (Uint8 *) screen->pixels;
+      int loc = 0;
+      for(int y = 0;y < height;y++) {
+        for(int x = 0;x < width;x++, pixel += bpp, loc++) {
+          if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+          {
+            continue;
+          }
+          Uint32 mapped = 0;
+          switch(bpp) {
+            case 1:
+              mapped = *pixel;
+              break;
+            case 2:
+              mapped = *(Uint16 *)pixel;
+              break;
+            case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+              mapped |= pixel[0] << 16;
+              mapped |= pixel[1] << 8;
+              mapped |= pixel[2] << 0;
+#else
+              mapped |= pixel[0] << 0;
+              mapped |= pixel[1] << 8;
+              mapped |= pixel[2] << 16;
+#endif
+              break;
+            case 4:
+              mapped = *(Uint32 *)pixel;
+              break;
+          }
+          Uint8 red, green, blue, alpha;
+          SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
+          red = (red * red_channel[loc]) >> 8;
+          green = (green * green_channel[loc]) >> 8;
+          blue = (blue * blue_channel[loc]) >> 8;
+          mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
+          switch(bpp) {
+            case 1:
+              *pixel = mapped;
+              break;
+            case 2:
+              *(Uint16 *)pixel = mapped;
+              break;
+            case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+              pixel[0] = (mapped >> 16) & 0xff;
+              pixel[1] = (mapped >> 8) & 0xff;
+              pixel[2] = (mapped >> 0) & 0xff;
+#else
+              pixel[0] = (mapped >> 0) & 0xff;
+              pixel[1] = (mapped >> 8) & 0xff;
+              pixel[2] = (mapped >> 16) & 0xff;
+#endif
+              break;
+            case 4:
+              *(Uint32 *)pixel = mapped;
+              break;
+          }
+        }
+        pixel += screen->pitch - width * bpp;
+      }
+      if(SDL_MUSTLOCK(screen))
+      {
+        SDL_UnlockSurface(screen);
+      }
+    }
+    else
+    {
+      int bpp = screen->format->BytesPerPixel;
+      if(SDL_MUSTLOCK(screen))
+      {
+        SDL_LockSurface(screen);
+      }
+      Uint8 *div_pixel = (Uint8 *) screen->pixels;
+      int loc = 0;
+      for(int y = 0;y < height;y++) {
+        for(int x = 0;x < width;x++, div_pixel += bpp * LIGHTMAP_DIV, loc++) {
+          if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
+          {
+            continue;
+          }
+          Uint8 *pixel = div_pixel;
+          for(int div_y = 0;div_y < LIGHTMAP_DIV;div_y++) {
+            for(int div_x = 0;div_x < LIGHTMAP_DIV;pixel += bpp, div_x++) {
+              Uint32 mapped = 0;
+              switch(bpp) {
+                case 1:
+                  mapped = *pixel;
+                  break;
+                case 2:
+                  mapped = *(Uint16 *)pixel;
+                  break;
+                case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+                  mapped |= pixel[0] << 16;
+                  mapped |= pixel[1] << 8;
+                  mapped |= pixel[2] << 0;
+#else
+                  mapped |= pixel[0] << 0;
+                  mapped |= pixel[1] << 8;
+                  mapped |= pixel[2] << 16;
+#endif
+                  break;
+                case 4:
+                  mapped = *(Uint32 *)pixel;
+                  break;
+              }
+              Uint8 red, green, blue, alpha;
+              SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
+
+#ifdef BILINEAR
+              int xinc = (x + 1 != width ? 1 : 0);
+              int yinc = (y + 1 != height ? width : 0);
+              Uint8 color00[3], color01[3], color10[3], color11[3];
+              {
+                color00[0] = red_channel[loc];
+                color00[1] = green_channel[loc];
+                color00[2] = blue_channel[loc];
+              }
+              {
+                color01[0] = red_channel[loc + xinc];
+                color01[1] = green_channel[loc + xinc];
+                color01[2] = blue_channel[loc + xinc];
+              }
+              {
+                color10[0] = red_channel[loc + yinc];
+                color10[1] = green_channel[loc + yinc];
+                color10[2] = blue_channel[loc + yinc];
+              }
+              {
+                color11[0] = red_channel[loc + yinc + xinc];
+                color11[1] = green_channel[loc + yinc + xinc];
+                color11[2] = blue_channel[loc + yinc + xinc];
+              }
+              Uint8 color0[3], color1[3], color[3];
+              merge(color0, color00, color01, div_x, LIGHTMAP_DIV);
+              merge(color1, color10, color11, div_x, LIGHTMAP_DIV);
+              merge(color, color0, color1, div_y, LIGHTMAP_DIV);
+              red = (red * color[0]) >> 8;
+              green = (green * color[1]) >> 8;
+              blue = (blue * color[2]) >> 8;
+#else
+              red = (red * red_channel[loc]) >> 8;
+              green = (green * green_channel[loc]) >> 8;
+              blue = (blue * blue_channel[loc]) >> 8;
+#endif
+
+              mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
+              switch(bpp) {
+                case 1:
+                  *pixel = mapped;
+                  break;
+                case 2:
+                  *(Uint16 *)pixel = mapped;
+                  break;
+                case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+                  pixel[0] = (mapped >> 16) & 0xff;
+                  pixel[1] = (mapped >> 8) & 0xff;
+                  pixel[2] = (mapped >> 0) & 0xff;
+#else
+                  pixel[0] = (mapped >> 0) & 0xff;
+                  pixel[1] = (mapped >> 8) & 0xff;
+                  pixel[2] = (mapped >> 16) & 0xff;
+#endif
+                  break;
+                case 4:
+                  *(Uint32 *)pixel = mapped;
+                  break;
+              }
+            }
+            pixel += screen->pitch - LIGHTMAP_DIV * bpp;
+          }
+        }
+        div_pixel += (screen->pitch - width * bpp) * LIGHTMAP_DIV;
+      }
+      if(SDL_MUSTLOCK(screen))
+      {
+        SDL_UnlockSurface(screen);
+      }
+    }
+  }
+
+  void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
+  {
+    dstx /= LIGHTMAP_DIV;
+    dsty /= LIGHTMAP_DIV;
+    int srcx = src_rect->x / LIGHTMAP_DIV;
+    int srcy = src_rect->y / LIGHTMAP_DIV;
+    int blit_width = src_rect->w / LIGHTMAP_DIV;
+    int blit_height = src_rect->h / LIGHTMAP_DIV;
+    int bpp = src->format->BytesPerPixel;
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_LockSurface(src);
+    }
+    Uint8 *pixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
+    int loc = dsty * width + dstx;
+    for(int y = 0;y < blit_height;y++) {
+      for(int x = 0;x < blit_width;x++, pixel += bpp * LIGHTMAP_DIV, loc++) {
+        if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
+        {
+          continue;
+        }
         if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
         {
           continue;
         }
+
         Uint32 mapped = 0;
         switch(bpp) {
           case 1:
@@ -112,46 +356,44 @@ namespace SDL
             break;
         }
         Uint8 red, green, blue, alpha;
-        SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
-        red = (red * red_channel[loc]) >> 8;
-        green = (green * green_channel[loc]) >> 8;
-        blue = (blue * blue_channel[loc]) >> 8;
-        mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
-        switch(bpp) {
-          case 1:
-            *pixel = mapped;
-            break;
-          case 2:
-            *(Uint16 *)pixel = mapped;
-            break;
-          case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
-            pixel[0] = (mapped >> 16) & 0xff;
-            pixel[1] = (mapped >> 8) & 0xff;
-            pixel[2] = (mapped >> 0) & 0xff;
-#else
-            pixel[0] = (mapped >> 0) & 0xff;
-            pixel[1] = (mapped >> 8) & 0xff;
-            pixel[2] = (mapped >> 16) & 0xff;
-#endif
-            break;
-          case 4:
-            *(Uint32 *)pixel = mapped;
-            break;
+        SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
+
+        if(red != 0)
+        {
+          int redsum = red_channel[loc] + (red * alpha >> 8);
+          red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+        }
+        if(green != 0)
+        {
+          int greensum = green_channel[loc] + (green * alpha >> 8);
+          green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+        }
+        if(blue != 0)
+        {
+          int bluesum = blue_channel[loc] + (blue * alpha >> 8);
+          blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
         }
       }
-      pixel += screen->pitch - width * bpp;
+      pixel += (src->pitch - blit_width * bpp) * LIGHTMAP_DIV;
+      loc += width - blit_width;
+    }
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_UnlockSurface(src);
     }
   }
 
-  void Lightmap::light_blit(SDL_Surface *src, int dstx, int dsty,
-                            int srcx, int srcy, int blit_width, int blit_height)
+  /*void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
   {
     int bpp = src->format->BytesPerPixel;
-      Uint8 *pixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_LockSurface(src);
+    }
+    Uint8 *pixel = (Uint8 *) src->pixels + src_rect->y * src->pitch + src_rect->x * bpp;
     int loc = dsty * width + dstx;
-    for(int y = 0;y < blit_height;y++) {
-      for(int x = 0;x < blit_width;x++, pixel += bpp, loc++) {
+    for(int y = 0;y < src_rect->h;y++) {
+      for(int x = 0;x < src_rect->w;x++, pixel += bpp, loc++) {
         if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
         {
           continue;
@@ -203,10 +445,14 @@ namespace SDL
           blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
         }
       }
-      pixel += src->pitch - blit_width * bpp;
-      loc += width - blit_width;
+      pixel += src->pitch - src_rect->w * bpp;
+      loc += width - src_rect->w;
     }
-  }
+    if(SDL_MUSTLOCK(src))
+    {
+      SDL_UnlockSurface(src);
+    }
+  }*/
 
   void
   Lightmap::draw_surface(const DrawingRequest& request)
@@ -219,6 +465,8 @@ namespace SDL
  
     const Surface* surface = (const Surface*) request.request_data;
     SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+    SDL::SurfaceData *surface_data = reinterpret_cast<SDL::SurfaceData *>(surface->get_surface_data());
+
     DrawingEffect effect = request.drawing_effect;
     if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
 
@@ -228,47 +476,12 @@ namespace SDL
     if (transform == 0) {
       std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
       return;
-    }  
-
-    int ox, oy;
-    if (effect == HORIZONTAL_FLIP)
-    {
-      ox = sdltexture->get_texture_width() - surface->get_x() - surface->get_width();
-    }
-    else
-    {
-      ox = surface->get_x();
-    }
-    if (effect == VERTICAL_FLIP)
-    {
-      oy = sdltexture->get_texture_height() - surface->get_y() - surface->get_height();
-    }
-    else
-    {
-      oy = surface->get_y();
-    }
-
-    int numerator, denominator;
-    float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
-    float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
-    if(xfactor < yfactor)
-    {
-      numerator = config->screenwidth;
-      denominator = SCREEN_WIDTH;
-    }
-    else
-    {
-      numerator = config->screenheight;
-      denominator = SCREEN_HEIGHT;
     }
 
+    SDL_Rect *src_rect = surface_data->get_src_rect(effect);
     int dstx = (int) request.pos.x * numerator / denominator;
     int dsty = (int) request.pos.y * numerator / denominator;
-    int srcx = ox * numerator / denominator;
-    int srcy = oy * numerator / denominator;
-    int blit_width = surface->get_width() * numerator / denominator;
-    int blit_height = surface->get_height() * numerator / denominator;
-    light_blit(transform, dstx, dsty, srcx, srcy, blit_width, blit_height);
+    light_blit(transform, src_rect, dstx, dsty);
   }
 
   void
@@ -279,6 +492,7 @@ namespace SDL
 
     const Surface* surface = surfacepartrequest->surface;
     SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
+
     DrawingEffect effect = request.drawing_effect;
     if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
 
@@ -288,7 +502,7 @@ namespace SDL
     if (transform == 0) {
       std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
       return;
-    }  
+    }
 
     int ox, oy;
     if (effect == HORIZONTAL_FLIP)
@@ -308,27 +522,14 @@ namespace SDL
       oy = surface->get_y();
     }
 
-    int numerator, denominator;
-    float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
-    float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
-    if(xfactor < yfactor)
-    {
-      numerator = config->screenwidth;
-      denominator = SCREEN_WIDTH;
-    }
-    else
-    {
-      numerator = config->screenheight;
-      denominator = SCREEN_HEIGHT;
-    }
-
+    SDL_Rect src_rect;
+    src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
+    src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
+    src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
+    src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
     int dstx = (int) request.pos.x * numerator / denominator;
     int dsty = (int) request.pos.y * numerator / denominator;
-    int srcx = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
-    int srcy = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
-    int blit_width = (int) surfacepartrequest->size.x * numerator / denominator;
-    int blit_height = (int) surfacepartrequest->size.y * numerator / denominator;
-    light_blit(transform, dstx, dsty, srcx, srcy, blit_width, blit_height);
+    light_blit(transform, &src_rect, dstx, dsty);
   }
 
   void
@@ -339,32 +540,34 @@ namespace SDL
     const Color& top = gradientrequest->top;
     const Color& bottom = gradientrequest->bottom;
 
+    int loc = 0;
     for(int y = 0;y < height;++y)
     {
-      Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
-      Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
-      Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
-      // FIXME
-      //Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
-      for(int x = 0;x < width;x++) {
-        int loc = y * width + x;
-        red_channel[loc] = std::min(red_channel[loc] + r, 255);
-        green_channel[loc] = std::min(green_channel[loc] + g, 255);
-        blue_channel[loc] = std::min(blue_channel[loc] + b, 255);
+      Uint8 red = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
+      Uint8 green = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
+      Uint8 blue = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
+      Uint8 alpha = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
+      for(int x = 0;x < width;x++, loc++) {
+        if(red != 0)
+        {
+          int redsum = red_channel[loc] + (red * alpha >> 8);
+          red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+        }
+        if(green != 0)
+        {
+          int greensum = green_channel[loc] + (green * alpha >> 8);
+          green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+        }
+        if(blue != 0)
+        {
+          int bluesum = blue_channel[loc] + (blue * alpha >> 8);
+          blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+        }
       }
     }
   }
 
   void
-  Lightmap::draw_text(const DrawingRequest& /*request*/)
-  {
-    //const TextRequest* textrequest = (TextRequest*) request.request_data;
-
-    //textrequest->font->draw(textrequest->text, request.pos,
-    //    textrequest->alignment, request.drawing_effect, request.alpha);
-  }
-
-  void
   Lightmap::draw_filled_rect(const DrawingRequest& request)
   {
     const FillRectRequest* fillrectrequest
@@ -384,9 +587,21 @@ namespace SDL
     for(int y = rect_y;y < rect_y + rect_h;y++) {
       for(int x = rect_x;x < rect_x + rect_w;x++) {
         int loc = y * width + x;
-        red_channel[loc] = std::min(red_channel[loc] + red, 255);
-        green_channel[loc] = std::min(green_channel[loc] + green, 255);
-        blue_channel[loc] = std::min(blue_channel[loc] + blue, 255);
+        if(red != 0)
+        {
+          int redsum = red_channel[loc] + red;
+          red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
+        }
+        if(green != 0)
+        {
+          int greensum = green_channel[loc] + green;
+          green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
+        }
+        if(blue != 0)
+        {
+          int bluesum = blue_channel[loc] + blue;
+          blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
+        }
       }
     }
   }