sdl_utils.cpp

Go to the documentation of this file.
00001 /* $Id: sdl_utils.cpp 26237 2008-04-29 19:46:55Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file sdl_utils.cpp
00016 //! Support-routines for the SDL-graphics-library.
00017 
00018 #include "global.hpp"
00019 
00020 #include "config.hpp"
00021 #include "log.hpp"
00022 #include "sdl_utils.hpp"
00023 #include "util.hpp"
00024 #include "video.hpp"
00025 
00026 #include <algorithm>
00027 #include <string.h>
00028 #include <cassert>
00029 #include <cmath>
00030 #include <iostream>
00031 #include <map>
00032 
00033 SDL_Color int_to_color(const Uint32 rgb) {
00034     SDL_Color to_return = {
00035         (0x00FF0000 & rgb)>>16,
00036         (0x0000FF00 & rgb)>>8,
00037         (0x000000FF & rgb), 0
00038     };
00039     return to_return;
00040 }
00041 
00042 Uint32 color_to_int(const SDL_Color& color) {
00043     Uint32 to_return = color.r;
00044     to_return = (to_return << 8) & color.g;
00045     to_return = (to_return << 8) & color.b;
00046     return to_return;
00047 }
00048 
00049 SDLKey sdl_keysym_from_name(std::string const &keyname)
00050 {
00051     static bool initialized = false;
00052     typedef std::map<std::string const, SDLKey> keysym_map_t;
00053     static keysym_map_t keysym_map;
00054 
00055     if (!initialized) {
00056         for(SDLKey i = SDLK_FIRST; i < SDLK_LAST; i = SDLKey(int(i) + 1)) {
00057             std::string name = SDL_GetKeyName(i);
00058             if (!name.empty())
00059                 keysym_map[name] = i;
00060         }
00061         initialized = true;
00062     }
00063 
00064     keysym_map_t::const_iterator it = keysym_map.find(keyname);
00065     if (it != keysym_map.end())
00066         return it->second;
00067     else
00068         return SDLK_UNKNOWN;
00069 }
00070 
00071 bool point_in_rect(int x, int y, const SDL_Rect& rect)
00072 {
00073     return x >= rect.x && y >= rect.y && x < rect.x + rect.w && y < rect.y + rect.h;
00074 }
00075 
00076 bool rects_overlap(const SDL_Rect& rect1, const SDL_Rect& rect2)
00077 {
00078     return (rect1.x < rect2.x+rect2.w && rect2.x < rect1.x+rect1.w &&
00079             rect1.y < rect2.y+rect2.h && rect2.y < rect1.y+rect1.h);
00080 }
00081 
00082 SDL_Rect intersect_rects(SDL_Rect const &rect1, SDL_Rect const &rect2)
00083 {
00084     SDL_Rect res;
00085     res.x = maximum<int>(rect1.x, rect2.x);
00086     res.y = maximum<int>(rect1.y, rect2.y);
00087     res.w = maximum<int>(minimum<int>(rect1.x + rect1.w, rect2.x + rect2.w) - res.x, 0);
00088     res.h = maximum<int>(minimum<int>(rect1.y + rect1.h, rect2.y + rect2.h) - res.y, 0);
00089     return res;
00090 }
00091 
00092 //! Creates an empty SDL_Rect.
00093 //!
00094 //! Since SDL_Rect can't be created as temp variable in one step create this wrapper.
00095 SDL_Rect create_rect(const int x, const int y, const int w, const int h)
00096 {
00097     SDL_Rect rect = { x, y, w, h };
00098     return rect;
00099 }
00100 
00101 bool operator<(const surface& a, const surface& b)
00102 {
00103     return a.get() < b.get();
00104 }
00105 
00106 static SDL_PixelFormat& get_neutral_pixel_format()
00107     {
00108         static bool first_time = true;
00109         static SDL_PixelFormat format;
00110 
00111         if(first_time) {
00112             first_time = false;
00113             surface surf(SDL_CreateRGBSurface(SDL_SWSURFACE,1,1,32,0xFF0000,0xFF00,0xFF,0xFF000000));
00114             format = *surf->format;
00115             format.palette = NULL;
00116         }
00117 
00118         return format;
00119     }
00120 
00121 surface make_neutral_surface(surface const &surf)
00122 {
00123     if(surf == NULL) {
00124         std::cerr << "null neutral surface...\n";
00125         return NULL;
00126     }
00127 
00128     surface const result = SDL_ConvertSurface(surf,&get_neutral_pixel_format(),SDL_SWSURFACE);
00129     if(result != NULL) {
00130         SDL_SetAlpha(result,SDL_SRCALPHA,SDL_ALPHA_OPAQUE);
00131     }
00132 
00133     return result;
00134 }
00135 
00136 
00137 surface create_optimized_surface(surface const &surf)
00138 {
00139     if(surf == NULL)
00140         return NULL;
00141 
00142     surface const result = display_format_alpha(surf);
00143     if(result == surf) {
00144         std::cerr << "resulting surface is the same as the source!!!\n";
00145     } else if(result == NULL) {
00146         return surf;
00147     }
00148 
00149     SDL_SetAlpha(result,SDL_SRCALPHA|SDL_RLEACCEL,SDL_ALPHA_OPAQUE);
00150 
00151     return result;
00152 }
00153 
00154 //! Streches a surface in the horizontal direction.
00155 //!
00156 //! The stretches a surface it uses the first pixel in the horizontal
00157 //! direction of the original surface and copies that to the destination.
00158 //! This means only the first column of the original is used for the destination.
00159 //! @param surf              The source surface.
00160 //! @param w                 The width of the resulting surface.
00161 //! @param optimize          Should the return surface be RLE optimized.
00162 //!
00163 //! @return                  An optimized surface.
00164 //!                          returned. 
00165 //! @retval 0                Returned upon error.
00166 //! @retval surf             Returned if w == surf->w, note this ignores the 
00167 //!                          optimize flag.
00168 surface stretch_surface_horizontal(
00169         const surface& surf, const unsigned w, const bool optimize)
00170 {
00171     // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
00172     assert(SDL_ALPHA_TRANSPARENT==0);
00173 
00174     if(surf == NULL)
00175         return NULL;
00176 
00177     if(w == surf->w) {
00178         return surf;
00179     }
00180     assert(w > 0);
00181 
00182     surface dst(SDL_CreateRGBSurface(SDL_SWSURFACE, 
00183         w, surf->h, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000));
00184         
00185     surface src(make_neutral_surface(surf));
00186     // Now both surfaces are always in the "neutral" pixel format
00187 
00188     if(src == NULL || dst == NULL) {
00189         std::cerr << "Could not create surface to scale onto\n";
00190         return NULL;
00191     }
00192 
00193     {
00194         // Extra scoping used for the surface_lock.
00195         surface_lock src_lock(src);
00196         surface_lock dst_lock(dst);
00197 
00198         Uint32* const src_pixels = reinterpret_cast<Uint32*>(src_lock.pixels());
00199         Uint32* dst_pixels = reinterpret_cast<Uint32*>(dst_lock.pixels());
00200 
00201         for(unsigned y = 0; y < src->h; ++y) {
00202             const Uint32 pixel = src_pixels [y * src->w];
00203             for(unsigned x = 0; x < w; ++x) {
00204 
00205                 *dst_pixels++ = pixel;
00206             
00207             }
00208         }
00209     }
00210 
00211     return optimize ? create_optimized_surface(dst) : dst;
00212 }
00213 
00214 //! Streches a surface in the vertical direction.
00215 //!
00216 //! The stretches a surface it uses the first pixel in the vertical
00217 //! direction of the original surface and copies that to the destination.
00218 //! This means only the first row of the original is used for the destination.
00219 //! @param surf              The source surface.
00220 //! @param h                 The height of the resulting surface.
00221 //! @param optimize          Should the return surface be RLE optimized.
00222 //!
00223 //! @return                  An optimized surface.
00224 //!                          returned. 
00225 //! @retval 0                Returned upon error.
00226 //! @retval surf             Returned if h == surf->h, note this ignores the 
00227 //!                          optimize flag.
00228 surface stretch_surface_vertical(
00229         const surface& surf, const unsigned h, const bool optimize)
00230 {
00231     // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
00232     assert(SDL_ALPHA_TRANSPARENT==0);
00233 
00234     if(surf == NULL)
00235         return NULL;
00236 
00237     if(h == surf->h) {
00238         return surf;
00239     }
00240     assert(h > 0);
00241 
00242     surface dst(SDL_CreateRGBSurface(SDL_SWSURFACE, 
00243         surf->w, h, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000));
00244         
00245     surface src(make_neutral_surface(surf));
00246     // Now both surfaces are always in the "neutral" pixel format
00247 
00248     if(src == NULL || dst == NULL) {
00249         std::cerr << "Could not create surface to scale onto\n";
00250         return NULL;
00251     }
00252 
00253     {
00254         // Extra scoping used for the surface_lock.
00255         surface_lock src_lock(src);
00256         surface_lock dst_lock(dst);
00257 
00258         Uint32* const src_pixels = reinterpret_cast<Uint32*>(src_lock.pixels());
00259         Uint32* dst_pixels = reinterpret_cast<Uint32*>(dst_lock.pixels());
00260 
00261         for(unsigned y = 0; y < h; ++y) {
00262             for(unsigned x = 0; x < src->w; ++x) {
00263 
00264                 *dst_pixels++ = src_pixels[x];
00265             }
00266         }
00267     }
00268 
00269     return optimize ? create_optimized_surface(dst) : dst;
00270 }
00271 
00272 // NOTE: Don't pass this function 0 scaling arguments.
00273 surface scale_surface(surface const &surf, int w, int h, bool optimize)
00274 {
00275     // Since SDL version 1.1.5 0 is transparent, before 255 was transparent.
00276     assert(SDL_ALPHA_TRANSPARENT==0);
00277 
00278     if(surf == NULL)
00279         return NULL;
00280 
00281     if(w == surf->w && h == surf->h) {
00282         return surf;
00283     }
00284     assert(w >= 0);
00285     assert(h >= 0);
00286 
00287     surface dst(SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,32,0xFF0000,0xFF00,0xFF,0xFF000000));
00288         
00289     if (w == 0 || h ==0) {
00290         std::cerr << "Create an empty image\n";
00291         return create_optimized_surface(dst);
00292     }
00293 
00294     surface src(make_neutral_surface(surf));
00295     // Now both surfaces are always in the "neutral" pixel format
00296 
00297     if(src == NULL || dst == NULL) {
00298         std::cerr << "Could not create surface to scale onto\n";
00299         return NULL;
00300     }
00301 
00302     const fixed_t xratio = fxpdiv(surf->w,w);
00303     const fixed_t yratio = fxpdiv(surf->h,h);
00304 
00305     {
00306         surface_lock src_lock(src);
00307         surface_lock dst_lock(dst);
00308 
00309         Uint32* const src_pixels = reinterpret_cast<Uint32*>(src_lock.pixels());
00310         Uint32* const dst_pixels = reinterpret_cast<Uint32*>(dst_lock.pixels());
00311 
00312         fixed_t ysrc = ftofxp(0.0);
00313         for(int ydst = 0; ydst != h; ++ydst, ysrc += yratio) {
00314             fixed_t xsrc = ftofxp(0.0);
00315             for(int xdst = 0; xdst != w; ++xdst, xsrc += xratio) {
00316                 const int xsrcint = fxptoi(xsrc);
00317                 const int ysrcint = fxptoi(ysrc);
00318 
00319                 Uint32* const src_word = src_pixels + ysrcint*src->w + xsrcint;
00320                 Uint32* const dst_word = dst_pixels +    ydst*dst->w + xdst;
00321                 const int dx = (xsrcint + 1 < src->w) ? 1 : 0;
00322                 const int dy = (ysrcint + 1 < src->h) ? src->w : 0;
00323 
00324                 Uint8 r,g,b,a;
00325                 Uint32 rr,gg,bb,aa;
00326                 Uint16 avg_r, avg_g, avg_b, avg_a;
00327                 Uint32 pix[4], bilin[4];
00328 
00329                 // This next part is the fixed point
00330                 // equivalent of "take everything to
00331                 // the right of the decimal point."
00332                 // These fundamental weights decide
00333                 // the contributions from various
00334                 // input pixels. The labels assume
00335                 // that the upper left corner of the
00336                 // screen ("northeast") is 0,0 but the
00337                 // code should still be consistant if
00338                 // the graphics origin is actually
00339                 // somewhere else.
00340 
00341                 const fixed_t e = 0x000000FF & xsrc;
00342                 const fixed_t s = 0x000000FF & ysrc;
00343                 const fixed_t n = 0xFF - s;
00344                 const fixed_t w = 0xFF - e;
00345 
00346                 pix[0] = *src_word;              // northwest
00347                 pix[1] = *(src_word + dx);       // northeast
00348                 pix[2] = *(src_word + dy);       // southwest
00349                 pix[3] = *(src_word + dx + dy);  // southeast
00350 
00351                 bilin[0] = n*w;
00352                 bilin[1] = n*e;
00353                 bilin[2] = s*w;
00354                 bilin[3] = s*e;
00355 
00356                 // Scope out the neighboorhood, see
00357                 // what the pixel values are like.
00358 
00359                 int count = 0;
00360                 avg_r = avg_g = avg_b = avg_a = 0;
00361                 int loc;
00362                 for (loc=0; loc<4; loc++) {
00363                   a = pix[loc] >> 24;
00364                   r = pix[loc] >> 16;
00365                   g = pix[loc] >> 8;
00366                   b = pix[loc] >> 0;
00367                   if (a != 0) {
00368                     avg_r += r;
00369                     avg_g += g;
00370                     avg_b += b;
00371                     avg_a += a;
00372                     count++;
00373                   }
00374                 }
00375                 if (count>0) {
00376                   avg_r /= count;
00377                   avg_b /= count;
00378                   avg_g /= count;
00379                   avg_a /= count;
00380                 }
00381 
00382                 // Perform modified bilinear interpolation.
00383                 // Don't trust any color information from
00384                 // an RGBA sample when the alpha channel
00385                 // is set to fully transparent.
00386                 //
00387                 // Some of the input images are hex tiles,
00388                 // created using a hexagon shaped alpha channel
00389                 // that is either set to full-on or full-off.
00390                 //
00391                 // If intermediate alpha values are introduced
00392                 // along a hex edge, it produces a gametime artifact.
00393                 // Moving the mouse around will leave behind
00394                 // "hexagon halos" from the temporary highlighting.
00395                 // In other words, the Wesnoth rendering engine
00396                 // freaks out.
00397                 //
00398                 // The alpha thresholding step attempts
00399                 // to accomodates this limitation.
00400                 // There is a small loss of quality.
00401                 // For example, skeleton bowstrings
00402                 // are not as good as they could be.
00403 
00404                 rr = gg = bb = aa = 0;
00405                 for (loc=0; loc<4; loc++) {
00406                   a = pix[loc] >> 24;
00407                   r = pix[loc] >> 16;
00408                   g = pix[loc] >> 8;
00409                   b = pix[loc] >> 0;
00410                   if (a == 0) {
00411                     r = avg_r;
00412                     g = avg_g;
00413                     b = avg_b;
00414                   }
00415                   rr += r * bilin[loc];
00416                   gg += g * bilin[loc];
00417                   bb += b * bilin[loc];
00418                   aa += a * bilin[loc];
00419                 }
00420                 r = rr >> 16;
00421                 g = gg >> 16;
00422                 b = bb >> 16;
00423                 a = aa >> 16;
00424                 a = (a < avg_a/2) ? 0 : avg_a;
00425                 *dst_word = (a << 24) + (r << 16) + (g << 8) + b;
00426             }
00427         }
00428     }
00429 
00430     return optimize ? create_optimized_surface(dst) : dst;
00431 }
00432 
00433 surface scale_surface_blended(surface const &surf, int w, int h, bool optimize)
00434 {
00435     if(surf== NULL)
00436         return NULL;
00437 
00438     if(w == surf->w && h == surf->h) {
00439         return surf;
00440     }
00441     assert(w >= 0);
00442     assert(h >= 0);
00443 
00444     surface dst(SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,32,0xFF0000,0xFF00,0xFF,0xFF000000));
00445 
00446     if (w == 0 || h ==0) {
00447         std::cerr << "Create an empty image\n";
00448         return create_optimized_surface(dst);
00449     }
00450 
00451     surface src(make_neutral_surface(surf));
00452 
00453     if(src == NULL || dst == NULL) {
00454         std::cerr << "Could not create surface to scale onto\n";
00455         return NULL;
00456     }
00457 
00458     const double xratio = static_cast<double>(surf->w)/
00459                           static_cast<double>(w);
00460     const double yratio = static_cast<double>(surf->h)/
00461                           static_cast<double>(h);
00462 
00463     {
00464         surface_lock src_lock(src);
00465         surface_lock dst_lock(dst);
00466 
00467         Uint32* const src_pixels = reinterpret_cast<Uint32*>(src_lock.pixels());
00468         Uint32* const dst_pixels = reinterpret_cast<Uint32*>(dst_lock.pixels());
00469 
00470         double ysrc = 0.0;
00471         for(int ydst = 0; ydst != h; ++ydst, ysrc += yratio) {
00472             double xsrc = 0.0;
00473             for(int xdst = 0; xdst != w; ++xdst, xsrc += xratio) {
00474                 double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
00475 
00476                 double summation = 0.0;
00477 
00478                 // We now have a rectangle, (xsrc,ysrc,xratio,yratio)
00479                 // which we want to derive the pixel from
00480                 for(double xloc = xsrc; xloc < xsrc+xratio; xloc += 1.0) {
00481                     const double xsize = minimum<double>(std::floor(xloc+1.0)-xloc,xsrc+xratio-xloc);
00482                     for(double yloc = ysrc; yloc < ysrc+yratio; yloc += 1.0) {
00483                         const int xsrcint = maximum<int>(0,minimum<int>(src->w-1,static_cast<int>(xsrc)));
00484                         const int ysrcint = maximum<int>(0,minimum<int>(src->h-1,static_cast<int>(ysrc)));
00485 
00486                         const double ysize = minimum<double>(std::floor(yloc+1.0)-yloc,ysrc+yratio-yloc);
00487 
00488                         Uint8 r,g,b,a;
00489 
00490                         SDL_GetRGBA(src_pixels[ysrcint*src->w + xsrcint],src->format,&r,&g,&b,&a);
00491                         const double value = xsize*ysize*double(a)/255.0;
00492                         summation += value;
00493 
00494                         red += r*value;
00495                         green += g*value;
00496                         blue += b*value;
00497                         alpha += a*value;
00498                     }
00499                 }
00500 
00501                 if(summation == 0.0)
00502                     summation = 1.0;
00503 
00504                 red /= summation;
00505                 green /= summation;
00506                 blue /= summation;
00507                 alpha /= summation;
00508 
00509                 dst_pixels[ydst*dst->w + xdst] = SDL_MapRGBA(dst->format,Uint8(red),Uint8(green),Uint8(blue),Uint8(alpha));
00510             }
00511         }
00512     }
00513 
00514     return optimize ? create_optimized_surface(dst) : dst;
00515 }
00516 
00517 surface adjust_surface_colour(surface const &surf, int red, int green, int blue, bool optimize)
00518 {
00519     if((red == 0 && green == 0 && blue == 0) || surf == NULL)
00520         return create_optimized_surface(surf);
00521 
00522     surface nsurf(make_neutral_surface(surf));
00523 
00524     if(nsurf == NULL) {
00525         std::cerr << "failed to make neutral surface\n";
00526         return NULL;
00527     }
00528 
00529     {
00530         surface_lock lock(nsurf);
00531         Uint32* beg = lock.pixels();
00532         Uint32* end = beg + nsurf->w*surf->h;
00533 
00534         while(beg != end) {
00535             Uint8 alpha = (*beg) >> 24;
00536 
00537             if(alpha) {
00538                 Uint8 r, g, b;
00539                 r = (*beg) >> 16;
00540                 g = (*beg) >> 8;
00541                 b = (*beg) >> 0;
00542 
00543                 r = maximum<int>(0,minimum<int>(255,int(r)+red));
00544                 g = maximum<int>(0,minimum<int>(255,int(g)+green));
00545                 b = maximum<int>(0,minimum<int>(255,int(b)+blue));
00546 
00547                 *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
00548             }
00549 
00550             ++beg;
00551         }
00552     }
00553 
00554     return optimize ? create_optimized_surface(nsurf) : nsurf;
00555 }
00556 
00557 surface greyscale_image(surface const &surf, bool optimize)
00558 {
00559     if(surf == NULL)
00560         return NULL;
00561 
00562     surface nsurf(make_neutral_surface(surf));
00563     if(nsurf == NULL) {
00564         std::cerr << "failed to make neutral surface\n";
00565         return NULL;
00566     }
00567 
00568     {
00569         surface_lock lock(nsurf);
00570         Uint32* beg = lock.pixels();
00571         Uint32* end = beg + nsurf->w*surf->h;
00572 
00573         while(beg != end) {
00574             Uint8 alpha = (*beg) >> 24;
00575 
00576             if(alpha) {
00577                 Uint8 r, g, b;
00578                 r = (*beg) >> 16;
00579                 g = (*beg) >> 8;
00580                 b = (*beg);
00581                 //const Uint8 avg = (red+green+blue)/3;
00582 
00583                 // Use the correct formula for RGB to grayscale conversion.
00584                 // Ok, this is no big deal :)
00585                 // The correct formula being:
00586                 // gray=0.299red+0.587green+0.114blue
00587                 const Uint8 avg = static_cast<Uint8>((
00588                     77  * static_cast<Uint16>(r) +
00589                     150 * static_cast<Uint16>(g) +
00590                     29  * static_cast<Uint16>(b)  ) / 256);
00591 
00592                 *beg = (alpha << 24) | (avg << 16) | (avg << 8) | avg;
00593             }
00594 
00595             ++beg;
00596         }
00597     }
00598 
00599     return optimize ? create_optimized_surface(nsurf) : nsurf;
00600 }
00601 
00602 surface darken_image(surface const &surf, bool optimize)
00603 {
00604     if(surf == NULL)
00605         return NULL;
00606 
00607     surface nsurf(make_neutral_surface(surf));
00608     if(nsurf == NULL) {
00609         std::cerr << "failed to make neutral surface\n";
00610         return NULL;
00611     }
00612 
00613     {
00614         surface_lock lock(nsurf);
00615         Uint32* beg = lock.pixels();
00616         Uint32* end = beg + nsurf->w*surf->h;
00617 
00618         while(beg != end) {
00619             Uint8 alpha = (*beg) >> 24;
00620 
00621             if(alpha) {
00622                 Uint8 r, g, b;
00623                 r = (*beg) >> 16;
00624                 g = (*beg) >> 8;
00625                 b = (*beg);
00626 
00627                 //const Uint8 avg = (red+green+blue)/3;
00628 
00629                 // Use the correct formula for RGB to grayscale conversion.
00630                 // Ok, this is no big deal :)
00631                 // The correct formula being:
00632                 // gray=0.299red+0.587green+0.114blue
00633                 const Uint8 avg = static_cast<Uint8>((
00634                     77  * static_cast<Uint16>(r) +
00635                     150 * static_cast<Uint16>(g) +
00636                     29  * static_cast<Uint16>(b)  ) / 256);
00637                 // then tint 77%, 67%, 72%
00638                 r = ((avg * 196) >> 8);
00639                 g = ((avg * 171) >> 8);
00640                 b = ((avg * 184) >> 8);
00641 
00642                 *beg = (alpha << 24) | (r << 16) | (g << 8) | b;
00643             }
00644 
00645             ++beg;
00646         }
00647     }
00648 
00649     return optimize ? create_optimized_surface(nsurf) : nsurf;
00650 }
00651 
00652 surface recolor_image(surface surf, const std::map<Uint32, Uint32>& map_rgb, bool optimize){
00653     if(map_rgb.size()){
00654         if(surf == NULL)
00655         return NULL;
00656 
00657          surface nsurf(make_neutral_surface(surf));
00658          if(nsurf == NULL) {
00659             std::cerr << "failed to make neutral surface\n";
00660             return NULL;
00661          }
00662 
00663         surface_lock lock(nsurf);
00664         Uint32* beg = lock.pixels();
00665         Uint32* end = beg + nsurf->w*surf->h;
00666 
00667         std::map<Uint32, Uint32>::const_iterator map_rgb_end = map_rgb.end();
00668 
00669         while(beg != end) {
00670             Uint8 alpha = (*beg) >> 24;
00671 
00672             if(alpha){  // don't recolor invisible pixels.
00673                 // palette use only RGB channels, so remove alpha
00674                 Uint32 oldrgb = (*beg) & 0x00FFFFFF;
00675                 std::map<Uint32, Uint32>::const_iterator i = map_rgb.find(oldrgb);
00676                 if(i != map_rgb.end()){
00677                     *beg = (alpha << 24) + i->second;
00678                 }
00679             }
00680         ++beg;
00681         }
00682 
00683         return optimize ? create_optimized_surface(nsurf) : nsurf;
00684     }
00685     return surf;
00686 }
00687 
00688 surface brighten_image(surface const &surf, fixed_t amount, bool optimize)
00689 {
00690     if(surf == NULL) {
00691         return NULL;
00692     }
00693 
00694     surface nsurf(make_neutral_surface(surf));
00695 
00696     if(nsurf == NULL) {
00697         std::cerr << "could not make neutral surface...\n";
00698         return NULL;
00699     }
00700 
00701     {
00702         surface_lock lock(nsurf);
00703         Uint32* beg = lock.pixels();
00704         Uint32* end = beg + nsurf->w*surf->h;
00705 
00706         if (amount < 0) amount = 0;
00707         while(beg != end) {
00708             Uint8 alpha = (*beg) >> 24;
00709 
00710             if(alpha) {
00711                 Uint8 r, g, b;
00712                 r = (*beg) >> 16;
00713                 g = (*beg) >> 8;
00714                 b = (*beg);
00715 
00716                 r = minimum<unsigned>(unsigned(fxpmult(r, amount)),255);
00717                 g = minimum<unsigned>(unsigned(fxpmult(g, amount)),255);
00718                 b = minimum<unsigned>(unsigned(fxpmult(b, amount)),255);
00719 
00720                 *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
00721             }
00722 
00723             ++beg;
00724         }
00725     }
00726 
00727     return optimize ? create_optimized_surface(nsurf) : nsurf;
00728 }
00729 
00730 surface adjust_surface_alpha(surface const &surf, fixed_t amount, bool optimize)
00731 {
00732     if(surf== NULL) {
00733         return NULL;
00734     }
00735 
00736     surface nsurf(make_neutral_surface(surf));
00737 
00738     if(nsurf == NULL) {
00739         std::cerr << "could not make neutral surface...\n";
00740         return NULL;
00741     }
00742 
00743     {
00744         surface_lock lock(nsurf);
00745         Uint32* beg = lock.pixels();
00746         Uint32* end = beg + nsurf->w*surf->h;
00747 
00748         if (amount < 0) amount = 0;
00749         while(beg != end) {
00750             Uint8 alpha = (*beg) >> 24;
00751 
00752             if(alpha) {
00753                 Uint8 r, g, b;
00754                 r = (*beg) >> 16;
00755                 g = (*beg) >> 8;
00756                 b = (*beg);
00757 
00758                 alpha = minimum<unsigned>(unsigned(fxpmult(alpha,amount)),255);
00759                 *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
00760             }
00761 
00762             ++beg;
00763         }
00764     }
00765 
00766     return optimize ? create_optimized_surface(nsurf) : nsurf;
00767 }
00768 
00769 surface adjust_surface_alpha_add(surface const &surf, int amount, bool optimize)
00770 {
00771     if(surf== NULL) {
00772         return NULL;
00773     }
00774 
00775     surface nsurf(make_neutral_surface(surf));
00776 
00777     if(nsurf == NULL) {
00778         std::cerr << "could not make neutral surface...\n";
00779         return NULL;
00780     }
00781 
00782     {
00783         surface_lock lock(nsurf);
00784         Uint32* beg = lock.pixels();
00785         Uint32* end = beg + nsurf->w*surf->h;
00786 
00787         while(beg != end) {
00788             Uint8 alpha = (*beg) >> 24;
00789 
00790             if(alpha) {
00791                 Uint8 r, g, b;
00792                 r = (*beg) >> 16;
00793                 g = (*beg) >> 8;
00794                 b = (*beg);
00795                 
00796                 alpha = Uint8(maximum<int>(0,minimum<int>(255,int(alpha) + amount)));
00797                 *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
00798             }
00799 
00800             ++beg;
00801         }
00802     }
00803 
00804     return optimize ? create_optimized_surface(nsurf) : nsurf;
00805 }
00806 
00807 //! Applies a mask on a surface.
00808 surface mask_surface(surface const &surf, surface const &mask)
00809 {
00810     if(surf == NULL) {
00811         return NULL;
00812     }
00813 
00814     surface nsurf = make_neutral_surface(surf);
00815     surface nmask(make_neutral_surface(mask));
00816 
00817     if(nsurf == NULL || nmask == NULL) {
00818         std::cerr << "could not make neutral surface...\n";
00819         return NULL;
00820     }
00821     if (nsurf->w !=  nmask->w) {
00822         // we don't support efficiently different width.
00823         // (different height is not a real problem)
00824         // This function is used on all hexes and usually only for that
00825         // so better keep it simple and efficient for the normal case
00826         std::cerr << "Detected an image with bad dimensions :" << nsurf->w << "x" << nsurf->h << "\n";
00827         std::cerr << "It will not be masked, please use :"<< nmask->w << "x" << nmask->h << "\n";
00828         return nsurf;
00829     }
00830     
00831     {
00832         surface_lock lock(nsurf);
00833         surface_lock mlock(nmask);
00834 
00835         Uint32* beg = lock.pixels();
00836         Uint32* end = beg + nsurf->w*surf->h;
00837         Uint32* mbeg = mlock.pixels();
00838         Uint32* mend = mbeg + nmask->w*nmask->h;
00839 
00840         while(beg != end && mbeg != mend) {
00841             Uint8 alpha = (*beg) >> 24;
00842 
00843             if(alpha) {
00844                 Uint8 r, g, b;
00845                 r = (*beg) >> 16;
00846                 g = (*beg) >> 8;
00847                 b = (*beg);
00848 
00849                 Uint8 malpha = (*mbeg) >> 24;
00850                 if (alpha > malpha) alpha = malpha;
00851 
00852                 *beg = (alpha << 24) + (r << 16) + (g << 8) + b;
00853             }
00854             
00855             ++beg;
00856             ++mbeg;
00857         }
00858     }
00859 
00860     return nsurf;
00861     //return create_optimized_surface(nsurf);
00862 }
00863 
00864 //! Cross-fades a surface.
00865 surface blur_surface(surface const &surf, int depth, bool optimize)
00866 {
00867     if(surf == NULL) {
00868         return NULL;
00869     }
00870 
00871     surface res = make_neutral_surface(surf);
00872 
00873     if(res == NULL) {
00874         std::cerr << "could not make neutral surface...\n";
00875         return NULL;
00876     }
00877 
00878     const int max_blur = 256;
00879     if(depth > max_blur) {
00880         depth = max_blur;
00881     }
00882 
00883     Uint32 queue[max_blur];
00884     const Uint32* end_queue = queue + max_blur;
00885 
00886     const Uint32 ff = 0xff;
00887 
00888     surface_lock lock(res);
00889     int x, y;
00890     for(y = 0; y < res->h; ++y) {
00891         const Uint32* front = &queue[0];
00892         Uint32* back = &queue[0];
00893         Uint32 red = 0, green = 0, blue = 0, avg = 0;
00894         Uint32* p = lock.pixels() + y*res->w;
00895         for(x = 0; x <= depth && x < res->w; ++x, ++p) {
00896             red += ((*p) >> 16)&0xFF;
00897             green += ((*p) >> 8)&0xFF;
00898             blue += (*p)&0xFF;
00899             ++avg;
00900             *back++ = *p;
00901             if(back == end_queue) {
00902                 back = &queue[0];
00903             }
00904         }
00905 
00906         p = lock.pixels() + y*res->w;
00907         for(x = 0; x < res->w; ++x, ++p) {
00908             *p = 0xFF000000 | (minimum(red/avg,ff) << 16) | (minimum(green/avg,ff) << 8) | minimum(blue/avg,ff);
00909             if(x >= depth) {
00910                 red -= ((*front) >> 16)&0xFF;
00911                 green -= ((*front) >> 8)&0xFF;
00912                 blue -= *front&0xFF;
00913                 --avg;
00914                 ++front;
00915                 if(front == end_queue) {
00916                     front = &queue[0];
00917                 }
00918             }
00919 
00920             if(x + depth+1 < res->w) {
00921                 Uint32* q = p + depth+1;
00922                 red += ((*q) >> 16)&0xFF;
00923                 green += ((*q) >> 8)&0xFF;
00924                 blue += (*q)&0xFF;
00925                 ++avg;
00926                 *back++ = *q;
00927                 if(back == end_queue) {
00928                     back = &queue[0];
00929                 }
00930             }
00931         }
00932     }
00933 
00934     for(x = 0; x < res->w; ++x) {
00935         const Uint32* front = &queue[0];
00936         Uint32* back = &queue[0];
00937         Uint32 red = 0, green = 0, blue = 0, avg = 0;
00938         Uint32* p = lock.pixels() + x;
00939         for(y = 0; y <= depth && y < res->h; ++y, p += res->w) {
00940             red += ((*p) >> 16)&0xFF;
00941             green += ((*p) >> 8)&0xFF;
00942             blue += *p&0xFF;
00943             ++avg;
00944             *back++ = *p;
00945             if(back == end_queue) {
00946                 back = &queue[0];
00947             }
00948         }
00949 
00950         p = lock.pixels() + x;
00951         for(y = 0; y < res->h; ++y, p += res->w) {
00952             *p = 0xFF000000 | (minimum(red/avg,ff) << 16) | (minimum(green/avg,ff) << 8) | minimum(blue/avg,ff);
00953             if(y >= depth) {
00954                 red -= ((*front) >> 16)&0xFF;
00955                 green -= ((*front) >> 8)&0xFF;
00956                 blue -= *front&0xFF;
00957                 --avg;
00958                 ++front;
00959                 if(front == end_queue) {
00960                     front = &queue[0];
00961                 }
00962             }
00963 
00964             if(y + depth+1 < res->h) {
00965                 Uint32* q = p + (depth+1)*res->w;
00966                 red += ((*q) >> 16)&0xFF;
00967                 green += ((*q) >> 8)&0xFF;
00968                 blue += (*q)&0xFF;
00969                 ++avg;
00970                 *back++ = *q;
00971                 if(back == end_queue) {
00972                     back = &queue[0];
00973                 }
00974             }
00975         }
00976     }
00977 
00978     return optimize ? create_optimized_surface(res) : res;
00979 }
00980 
00981 //! Cross-fades a surface with alpha channel.
00982 //! @todo FIXME: This is just an adapted copy-paste
00983 //! of the normal blur but with blur alpha channel too
00984 surface blur_alpha_surface(surface const &surf, int depth, bool optimize)
00985 {
00986     if(surf == NULL) {
00987         return NULL;
00988     }
00989 
00990     surface res = make_neutral_surface(surf);
00991 
00992     if(res == NULL) {
00993         std::cerr << "could not make neutral surface...\n";
00994         return NULL;
00995     }
00996 
00997     const int max_blur = 256;
00998     if(depth > max_blur) {
00999         depth = max_blur;
01000     }
01001 
01002     Uint32 queue[max_blur];
01003     const Uint32* end_queue = queue + max_blur;
01004 
01005     const Uint32 ff = 0xff;
01006 
01007     surface_lock lock(res);
01008     int x, y;
01009     for(y = 0; y < res->h; ++y) {
01010         const Uint32* front = &queue[0];
01011         Uint32* back = &queue[0];
01012         Uint32 alpha=0, red = 0, green = 0, blue = 0, avg = 0;
01013         Uint32* p = lock.pixels() + y*res->w;
01014         for(x = 0; x <= depth && x < res->w; ++x, ++p) {
01015             alpha += ((*p) >> 24)&0xFF;
01016             red += ((*p) >> 16)&0xFF;
01017             green += ((*p) >> 8)&0xFF;
01018             blue += (*p)&0xFF;
01019             ++avg;
01020             *back++ = *p;
01021             if(back == end_queue) {
01022                 back = &queue[0];
01023             }
01024         }
01025 
01026         p = lock.pixels() + y*res->w;
01027         for(x = 0; x < res->w; ++x, ++p) {
01028             *p = (minimum(alpha/avg,ff) << 24) | (minimum(red/avg,ff) << 16) | (minimum(green/avg,ff) << 8) | minimum(blue/avg,ff);
01029             if(x >= depth) {
01030                 alpha -= ((*front) >> 24)&0xFF;
01031                 red -= ((*front) >> 16)&0xFF;
01032                 green -= ((*front) >> 8)&0xFF;
01033                 blue -= *front&0xFF;
01034                 --avg;
01035                 ++front;
01036                 if(front == end_queue) {
01037                     front = &queue[0];
01038                 }
01039             }
01040 
01041             if(x + depth+1 < res->w) {
01042                 Uint32* q = p + depth+1;
01043                 alpha += ((*q) >> 24)&0xFF;
01044                 red += ((*q) >> 16)&0xFF;
01045                 green += ((*q) >> 8)&0xFF;
01046                 blue += (*q)&0xFF;
01047                 ++avg;
01048                 *back++ = *q;
01049                 if(back == end_queue) {
01050                     back = &queue[0];
01051                 }
01052             }
01053         }
01054     }
01055 
01056     for(x = 0; x < res->w; ++x) {
01057         const Uint32* front = &queue[0];
01058         Uint32* back = &queue[0];
01059         Uint32 alpha=0, red = 0, green = 0, blue = 0, avg = 0;
01060         Uint32* p = lock.pixels() + x;
01061         for(y = 0; y <= depth && y < res->h; ++y, p += res->w) {
01062             alpha += ((*p) >> 24)&0xFF;
01063             red += ((*p) >> 16)&0xFF;
01064             green += ((*p) >> 8)&0xFF;
01065             blue += *p&0xFF;
01066             ++avg;
01067             *back++ = *p;
01068             if(back == end_queue) {
01069                 back = &queue[0];
01070             }
01071         }
01072 
01073         p = lock.pixels() + x;
01074         for(y = 0; y < res->h; ++y, p += res->w) {
01075             *p = (minimum(alpha/avg,ff) << 24) | (minimum(red/avg,ff) << 16) | (minimum(green/avg,ff) << 8) | minimum(blue/avg,ff);
01076             if(y >= depth) {
01077                 alpha -= ((*front) >> 24)&0xFF;
01078                 red -= ((*front) >> 16)&0xFF;
01079                 green -= ((*front) >> 8)&0xFF;
01080                 blue -= *front&0xFF;
01081                 --avg;
01082                 ++front;
01083                 if(front == end_queue) {
01084                     front = &queue[0];
01085                 }
01086             }
01087 
01088             if(y + depth+1 < res->h) {
01089                 Uint32* q = p + (depth+1)*res->w;
01090                 alpha += ((*q) >> 24)&0xFF;
01091                 red += ((*q) >> 16)&0xFF;
01092                 green += ((*q) >> 8)&0xFF;
01093                 blue += (*q)&0xFF;
01094                 ++avg;
01095                 *back++ = *q;
01096                 if(back == end_queue) {
01097                     back = &queue[0];
01098                 }
01099             }
01100         }
01101     }
01102 
01103     return optimize ? create_optimized_surface(res) : res;
01104 }
01105 
01106 //! Cuts a rectangle from a surface.
01107 surface cut_surface(surface const &surf, SDL_Rect const &r)
01108 {
01109     surface res = create_compatible_surface(surf, r.w, r.h);
01110 
01111     size_t sbpp = surf->format->BytesPerPixel;
01112     size_t spitch = surf->pitch;
01113     size_t rbpp = res->format->BytesPerPixel;
01114     size_t rpitch = res->pitch;
01115 
01116     // compute the areas to copy
01117     SDL_Rect src_rect = r;
01118     SDL_Rect dst_rect = { 0, 0, r.w, r.h };
01119 
01120     if (src_rect.x < 0) {
01121         if (src_rect.x + src_rect.w <= 0)
01122             return res;
01123         dst_rect.x -= src_rect.x;
01124         dst_rect.w += src_rect.x;
01125         src_rect.w += src_rect.x;
01126         src_rect.x = 0;
01127     }
01128     if (src_rect.y < 0) {
01129         if (src_rect.y + src_rect.h <= 0)
01130             return res;
01131         dst_rect.y -= src_rect.y;
01132         dst_rect.h += src_rect.y;
01133         src_rect.h += src_rect.y;
01134         src_rect.y = 0;
01135     }
01136 
01137     if(src_rect.x >= surf->w || src_rect.y >= surf->h)
01138         return res;
01139 
01140     surface_lock slock(surf);
01141     surface_lock rlock(res);
01142 
01143     Uint8* src = reinterpret_cast<Uint8 *>(slock.pixels());
01144     Uint8* dest = reinterpret_cast<Uint8 *>(rlock.pixels());
01145 
01146     for(int y = 0; y < src_rect.h && (src_rect.y + y) < surf->h; ++y) {
01147         Uint8* line_src  = src  + (src_rect.y + y) * spitch + src_rect.x * sbpp;
01148         Uint8* line_dest = dest + (dst_rect.y + y) * rpitch + dst_rect.x * rbpp;
01149         size_t size = src_rect.w + src_rect.x <= surf->w ? src_rect.w : surf->w - src_rect.x;
01150 
01151         assert(rpitch >= src_rect.w * rbpp);
01152         memcpy(line_dest, line_src, size * rbpp);
01153     }
01154 
01155     return res;
01156 }
01157 
01158 surface blend_surface(surface const &surf, double amount, Uint32 colour, bool optimize)
01159 {
01160     if(surf== NULL) {
01161         return NULL;
01162     }
01163 
01164     surface nsurf(make_neutral_surface(surf));
01165 
01166     if(nsurf == NULL) {
01167         std::cerr << "could not make neutral surface...\n";
01168         return NULL;
01169     }
01170 
01171     {
01172         surface_lock lock(nsurf);
01173         Uint32* beg = lock.pixels();
01174         Uint32* end = beg + nsurf->w*surf->h;
01175 
01176         Uint8 red, green, blue, alpha;
01177         SDL_GetRGBA(colour,nsurf->format,&red,&green,&blue,&alpha);
01178 
01179         red   = Uint8(red   * amount);
01180         green = Uint8(green * amount);
01181         blue  = Uint8(blue  * amount);
01182 
01183         amount = 1.0 - amount;
01184 
01185         while(beg != end) {
01186             Uint8 r, g, b, a;
01187             a = (*beg) >> 24;
01188             r = (*beg) >> 16;
01189             g = (*beg) >> 8;
01190             b = (*beg);
01191 
01192             r = Uint8(r * amount) + red;
01193             g = Uint8(g * amount) + green;
01194             b = Uint8(b * amount) + blue;
01195 
01196             *beg = (a << 24) | (r << 16) | (g << 8) | b;
01197 
01198             ++beg;
01199         }
01200     }
01201 
01202     return optimize ? create_optimized_surface(nsurf) : nsurf;
01203 }
01204 
01205 surface flip_surface(surface const &surf, bool optimize)
01206 {
01207     if(surf == NULL) {
01208         return NULL;
01209     }
01210 
01211     surface nsurf(make_neutral_surface(surf));
01212 
01213     if(nsurf == NULL) {
01214         std::cerr << "could not make neutral surface...\n";
01215         return NULL;
01216     }
01217 
01218     {
01219         surface_lock lock(nsurf);
01220         Uint32* const pixels = lock.pixels();
01221 
01222         for(int y = 0; y != nsurf->h; ++y) {
01223             for(int x = 0; x != nsurf->w/2; ++x) {
01224                 const int index1 = y*nsurf->w + x;
01225                 const int index2 = (y+1)*nsurf->w - x - 1;
01226                 std::swap(pixels[index1],pixels[index2]);
01227             }
01228         }
01229     }
01230 
01231     return optimize ? create_optimized_surface(nsurf) : nsurf;
01232 }
01233 
01234 surface flop_surface(surface const &surf, bool optimize)
01235 {
01236     if(surf == NULL) {
01237         return NULL;
01238     }
01239 
01240     surface nsurf(make_neutral_surface(surf));
01241 
01242     if(nsurf == NULL) {
01243         std::cerr << "could not make neutral surface...\n";
01244         return NULL;
01245     }
01246 
01247     {
01248         surface_lock lock(nsurf);
01249         Uint32* const pixels = lock.pixels();
01250 
01251         for(int x = 0; x != nsurf->w; ++x) {
01252             for(int y = 0; y != nsurf->h/2; ++y) {
01253                 const int index1 = y*nsurf->w + x;
01254                 const int index2 = (nsurf->h-y-1)*surf->w + x;
01255                 std::swap(pixels[index1],pixels[index2]);
01256             }
01257         }
01258     }
01259 
01260     return optimize ? create_optimized_surface(nsurf) : nsurf;
01261 }
01262 
01263 
01264 surface create_compatible_surface(surface const &surf, int width, int height)
01265 {
01266     if(surf == NULL)
01267         return NULL;
01268 
01269     if(width == -1)
01270         width = surf->w;
01271 
01272     if(height == -1)
01273         height = surf->h;
01274 
01275     return SDL_CreateRGBSurface(SDL_SWSURFACE,width,height,surf->format->BitsPerPixel,
01276                                 surf->format->Rmask,surf->format->Gmask,surf->format->Bmask,surf->format->Amask);
01277 }
01278 
01279 //! Replacement for SDL_BlitSurface.
01280 //!
01281 //! SDL_BlitSurface has problems with blitting partly transparent surfaces so
01282 //! this is a replacement. It ignores the SDL_SRCALPHA and SDL_SRCCOLORKEY 
01283 //! flags. src and dst will have the SDL_RLEACCEL flag removed.
01284 //! The return value of SDL_BlistSurface is normally ignored so no return value.
01285 //! The rectangles are const and will not be modified.
01286 //!
01287 //! @param src          The surface to blit.
01288 //! @param srcrect      The region of the surface to blit
01289 //! @param dst          The surface to blit on.
01290 //! @param dstrect      The offset to blit the surface on, only x and y are used.
01291 void blit_surface(const surface& src, 
01292     const SDL_Rect* srcrect, surface& dst, const SDL_Rect* dstrect)
01293 {
01294     assert(src);
01295     assert(dst);
01296     assert((src->flags & SDL_RLEACCEL) == 0);
01297     assert((dst->flags & SDL_RLEACCEL) == 0);
01298 
01299     // Get the areas to blit
01300     SDL_Rect dst_rect = { 0, 0, dst->w, dst->h };
01301     if(dstrect) {
01302         dst_rect.x = dstrect->x;
01303         dst_rect.w -= dstrect->x;
01304 
01305         dst_rect.y = dstrect->y;
01306         dst_rect.h -= dstrect->y;
01307 
01308     }
01309 
01310     SDL_Rect src_rect = { 0, 0, src->w, src->h };
01311     if(srcrect && srcrect->w && srcrect->h) { 
01312         src_rect.x = srcrect->x;
01313         src_rect.y = srcrect->y;
01314 
01315         src_rect.w = srcrect->w;
01316         src_rect.h = srcrect->h;
01317 
01318         if (src_rect.x < 0) {
01319             if (src_rect.x + src_rect.w <= 0 || src_rect.x + dst_rect.w <= 0 )
01320                 return;
01321             dst_rect.x -= src_rect.x;
01322             dst_rect.w += src_rect.x;
01323             src_rect.w += src_rect.x;
01324             src_rect.x = 0;
01325         }
01326         if (src_rect.y < 0) {
01327             if (src_rect.y + src_rect.h <= 0 || src_rect.y + dst_rect.h <= 0 )
01328                 return;
01329             dst_rect.y -= src_rect.y;
01330             dst_rect.h += src_rect.y;
01331             src_rect.h += src_rect.y;
01332             src_rect.y = 0;
01333         }
01334         if (src_rect.x + src_rect.w > src->w) {
01335             if (src_rect.x >= src->w)
01336                 return;
01337             src_rect.w = src->w - src_rect.x;
01338         }
01339         if (src_rect.y + src_rect.h > src->h) {
01340             if (src_rect.y >= src->h)
01341                 return;
01342             src_rect.h = src->h - src_rect.y;
01343         }
01344     }
01345 
01346     assert(dst_rect.x >= 0);
01347     assert(dst_rect.y >= 0);
01348 
01349     // Get the blit size limits.
01350     const unsigned width = minimum(src_rect.w, dst_rect.w);
01351     const unsigned height = minimum(src_rect.h, dst_rect.h);
01352 //  std::cout << width << " -- " << height << "\n";
01353 //  std::cout << src->w << " -- " << src->h << "\n";
01354 //  std::cout << srcrect->x << "," << srcrect->y << " - " << srcrect->w << "x" << srcrect->h << " - " "\n";
01355 //  if (dstrect)
01356 //      std::cout << dstrect->x << "," << dstrect->y << " - " << dstrect->w << "x" << dstrect->h << " - " "\n";
01357 //  std::cout << src_rect.x << "," << src_rect.y << " - " << src_rect.w << "x" << src_rect.h << " - " "\n";
01358 //  std::cout << dst_rect.x << "," << dst_rect.y << " - " << dst_rect.w << "x" << dst_rect.h << " - " "\n";
01359 
01360     {
01361         // Extra scoping used for the surface_lock.
01362         surface_lock src_lock(src);
01363         surface_lock dst_lock(dst);
01364 
01365         Uint32* const src_pixels = reinterpret_cast<Uint32*>(src_lock.pixels());
01366         Uint32* dst_pixels = reinterpret_cast<Uint32*>(dst_lock.pixels());
01367 
01368         for(unsigned y = 0; y < height; ++y) {
01369             for(unsigned x = 0; x < width; ++x) {
01370 
01371                 // We need to do the blitting using some optimizations
01372                 // if the src is fully transparent we can ignore this pixel 
01373                 // if the src is fully opaque we can overwrite the destination with this pixel
01374                 // if the destination is fully transparent we replace us with the source
01375                 //
01376                 // We do these optimizations between the extraction of the variables
01377                 // to avoid creating variables not used (it might save us some cycles).
01378 
01379                 const Uint32 src_pixel = src_pixels[(y + src_rect.y) * src->w + (x + src_rect.x)];
01380                 const Uint8 src_a = (src_pixel & 0xFF000000) >> 24;
01381 
01382                 if(!src_a) {
01383                     // Fully transparent source, ignore
01384                     continue;
01385                 }
01386 
01387                 const ptrdiff_t dst_offset = (y + dst_rect.y) * dst->w + (x + dst_rect.x);
01388                 if(src_a == 255) {
01389                     // Fully opaque source, copy
01390                     dst_pixels[dst_offset] = src_pixel;
01391                     continue;
01392                 }
01393 
01394                 const Uint32 dst_pixel = dst_pixels[dst_offset];
01395                 Uint8 dst_a = (dst_pixel & 0xFF000000) >> 24;
01396 
01397                 if(!dst_a) {
01398                     // Fully transparent destination, copy
01399                     dst_pixels[dst_offset] = src_pixel;
01400                     continue;
01401                 }
01402 
01403                 const Uint8 src_r = (src_pixel & 0x00FF0000) >> 16;
01404                 const Uint8 src_g = (src_pixel & 0x0000FF00) >> 8;
01405                 const Uint8 src_b = src_pixel & 0x000000FF;
01406 
01407                 Uint8 dst_r = (dst_pixel & 0x00FF0000) >> 16;
01408                 Uint8 dst_g = (dst_pixel & 0x0000FF00) >> 8;
01409                 Uint8 dst_b = dst_pixel & 0x000000FF;
01410 
01411                 if(dst_a == 255) {
01412                     
01413                     // Destination fully opaque blend the source.
01414                     dst_r = (((src_r - dst_r) * src_a) >> 8 ) + dst_r;
01415                     dst_g = (((src_g - dst_g) * src_a) >> 8 ) + dst_g;
01416                     dst_b = (((src_b - dst_b) * src_a) >> 8 ) + dst_b;
01417 
01418                 } else {
01419 
01420                     // Destination and source party transparent.
01421 
01422                     // aquired the data now do the blitting
01423                     const unsigned tmp_a = 255 - src_a;
01424 
01425                     const unsigned tmp_r = 1 + (src_r * src_a) + (dst_r * tmp_a);
01426                     dst_r = (tmp_r + (tmp_r >> 8)) >> 8;
01427 
01428                     const unsigned tmp_g = 1 + (src_g * src_a) + (dst_g * tmp_a);
01429                     dst_g = (tmp_g + (tmp_g >> 8)) >> 8;
01430 
01431                     const unsigned tmp_b = 1 + (src_b * src_a) + (dst_b * tmp_a);
01432                     dst_b = (tmp_b + (tmp_b >> 8)) >> 8;
01433 
01434                     dst_a += (((255 - dst_a) * src_a) >> 8);
01435                 }
01436 
01437                 dst_pixels[dst_offset] = (dst_a << 24) | (dst_r << 16) | (dst_g << 8) | (dst_b);
01438 
01439             }
01440         }
01441     }
01442 }
01443 
01444 
01445 
01446 void fill_rect_alpha(SDL_Rect &rect, Uint32 colour, Uint8 alpha, surface const &target)
01447 {
01448     if(alpha == SDL_ALPHA_OPAQUE) {
01449         SDL_FillRect(target,&rect,colour);
01450         return;
01451     } else if(alpha == SDL_ALPHA_TRANSPARENT) {
01452         return;
01453     }
01454 
01455     surface tmp(create_compatible_surface(target,rect.w,rect.h));
01456     if(tmp == NULL) {
01457         return;
01458     }
01459 
01460     SDL_Rect r = {0,0,rect.w,rect.h};
01461     SDL_FillRect(tmp,&r,colour);
01462     SDL_SetAlpha(tmp,SDL_SRCALPHA,alpha);
01463     SDL_BlitSurface(tmp,NULL,target,&rect);
01464 }
01465 
01466 surface get_surface_portion(surface const &src, SDL_Rect &area)
01467 {
01468     // Check if there is something in the portion
01469     if(area.x >= src->w || area.y >= src->h || area.x + area.w < 0 || area.y + area.h < 0) {
01470         //std::cerr << "illegal surface portion...\n";
01471         return NULL;
01472     }
01473 
01474     if(area.x + area.w > src->w) {
01475         area.w = src->w - area.x;
01476     }
01477     if(area.y + area.h > src->h) {
01478         area.h = src->h - area.y;
01479     }
01480 
01481     surface const dst = create_compatible_surface(src,area.w,area.h);
01482     if(dst == NULL) {
01483         std::cerr << "Could not create a new surface in get_surface_portion()\n";
01484         return NULL;
01485     }
01486 
01487     SDL_BlitSurface(src,&area,dst, NULL);
01488 
01489     return dst;
01490 }
01491 
01492 namespace {
01493 
01494 struct not_alpha
01495 {
01496     not_alpha() {}
01497 
01498     // we assume neutral format
01499     bool operator()(Uint32 pixel) const {
01500         Uint8 alpha = pixel >> 24;
01501         return alpha != 0x00;
01502     }
01503 };
01504 
01505 }
01506 
01507 SDL_Rect get_non_transparent_portion(surface const &surf)
01508 {
01509     SDL_Rect res = {0,0,0,0};
01510     const surface nsurf(make_neutral_surface(surf));
01511     if(nsurf == NULL) {
01512         std::cerr << "failed to make neutral surface\n";
01513         return res;
01514     }
01515 
01516     const not_alpha calc;
01517 
01518     surface_lock lock(nsurf);
01519     const Uint32* const pixels = lock.pixels();
01520 
01521     int n;
01522     for(n = 0; n != nsurf->h; ++n) {
01523         const Uint32* const start_row = pixels + n*nsurf->w;
01524         const Uint32* const end_row = start_row + nsurf->w;
01525 
01526         if(std::find_if(start_row,end_row,calc) != end_row)
01527             break;
01528     }
01529 
01530     res.y = n;
01531 
01532     for(n = 0; n != nsurf->h-res.y; ++n) {
01533         const Uint32* const start_row = pixels + (nsurf->h-n-1)*surf->w;
01534         const Uint32* const end_row = start_row + nsurf->w;
01535 
01536         if(std::find_if(start_row,end_row,calc) != end_row)
01537             break;
01538     }
01539 
01540     // The height is the height of the surface,
01541     // minus the distance from the top and
01542     // the distance from the bottom.
01543     res.h = nsurf->h - res.y - n;
01544 
01545     for(n = 0; n != nsurf->w; ++n) {
01546         int y;
01547         for(y = 0; y != nsurf->h; ++y) {
01548             const Uint32 pixel = pixels[y*nsurf->w + n];
01549             if(calc(pixel))
01550                 break;
01551         }
01552 
01553         if(y != nsurf->h)
01554             break;
01555     }
01556 
01557     res.x = n;
01558 
01559     for(n = 0; n != nsurf->w-res.x; ++n) {
01560         int y;
01561         for(y = 0; y != nsurf->h; ++y) {
01562             const Uint32 pixel = pixels[y*nsurf->w + surf->w - n - 1];
01563             if(calc(pixel))
01564                 break;
01565         }
01566 
01567         if(y != nsurf->h)
01568             break;
01569     }
01570 
01571     res.w = nsurf->w - res.x - n;
01572 
01573     return res;
01574 }
01575 
01576 bool operator==(const SDL_Rect& a, const SDL_Rect& b)
01577 {
01578     return a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h;
01579 }
01580 
01581 bool operator!=(const SDL_Rect& a, const SDL_Rect& b)
01582 {
01583     return !operator==(a,b);
01584 }
01585 
01586 bool operator==(const SDL_Color& a, const SDL_Color& b) {
01587     return a.r == b.r && a.g == b.g && a.b == b.b;
01588 }
01589 
01590 bool operator!=(const SDL_Color& a, const SDL_Color& b) {
01591     return !operator==(a,b);
01592 }
01593 
01594 SDL_Color inverse(const SDL_Color& colour) {
01595     SDL_Color inverse;
01596     inverse.r = 255 - colour.r;
01597     inverse.g = 255 - colour.g;
01598     inverse.b = 255 - colour.b;
01599     inverse.unused = 0;
01600     return inverse;
01601 }
01602 
01603 void pixel_data::read(const config& cfg) {
01604     const std::string& red = cfg["red"];
01605     const std::string& green = cfg["green"];
01606     const std::string& blue = cfg["blue"];
01607 
01608     if (red.empty())
01609         r = 0;
01610     else
01611         r = atoi(red.c_str());
01612 
01613     if (green.empty())
01614         g = 0;
01615     else
01616         g = atoi(green.c_str());
01617 
01618     if (blue.empty())
01619         b = 0;
01620     else
01621         b = atoi(blue.c_str());
01622 }
01623 
01624 surface_restorer::surface_restorer() : target_(NULL), rect_(empty_rect), surface_(NULL)
01625 {
01626 }
01627 
01628 surface_restorer::surface_restorer(CVideo* target, const SDL_Rect& rect)
01629 : target_(target), rect_(rect), surface_(NULL)
01630 {
01631     update();
01632 }
01633 
01634 surface_restorer::~surface_restorer()
01635 {
01636     restore();
01637 }
01638 
01639 void surface_restorer::restore(SDL_Rect const &dst) const
01640 {
01641     if (surface_.null())
01642         return;
01643     SDL_Rect dst2 = intersect_rects(dst, rect_);
01644     if (dst2.w == 0 || dst2.h == 0)
01645         return;
01646     SDL_Rect src = dst2;
01647     src.x -= rect_.x;
01648     src.y -= rect_.y;
01649     SDL_BlitSurface(surface_, &src, target_->getSurface(), &dst2);
01650     update_rect(dst2);
01651 }
01652 
01653 void surface_restorer::restore() const
01654 {
01655     if (surface_.null())
01656         return;
01657     SDL_Rect dst = rect_;
01658     SDL_BlitSurface(surface_, NULL, target_->getSurface(), &dst);
01659     update_rect(rect_);
01660 }
01661 
01662 void surface_restorer::update()
01663 {
01664     if(rect_.w == 0 || rect_.h == 0)
01665         surface_.assign(NULL);
01666     else
01667         surface_.assign(::get_surface_portion(target_->getSurface(),rect_));
01668 }
01669 
01670 void surface_restorer::cancel()
01671 {
01672     surface_.assign(NULL);
01673 }
01674 
01675 void draw_rectangle(int x, int y, int w, int h, Uint32 colour,surface target)
01676 {
01677 
01678     SDL_Rect top = {x,y,w,1};
01679     SDL_Rect bot = {x,y+h-1,w,1};
01680     SDL_Rect left = {x,y,1,h};
01681     SDL_Rect right = {x+w-1,y,1,h};
01682 
01683     SDL_FillRect(target,&top,colour);
01684     SDL_FillRect(target,&bot,colour);
01685     SDL_FillRect(target,&left,colour);
01686     SDL_FillRect(target,&right,colour);
01687 }
01688 
01689 void draw_solid_tinted_rectangle(int x, int y, int w, int h,
01690                                  int r, int g, int b,
01691                                  double alpha, surface target)
01692 {
01693 
01694     SDL_Rect rect = {x,y,w,h};
01695     fill_rect_alpha(rect,SDL_MapRGB(target->format,r,g,b),Uint8(alpha*255),target);
01696 }
01697 
01698 void draw_centered_on_background(surface surf, const SDL_Rect& rect, const SDL_Color& color, surface target)
01699 {
01700     clip_rect_setter clip_setter(target, rect);
01701 
01702     Uint32 col = SDL_MapRGBA(target->format, color.r, color.g, color.b, color.unused);
01703     //TODO: only draw background outside the image
01704     SDL_Rect r = rect;
01705     SDL_FillRect(target, &r, col);
01706 
01707     if (surf != NULL) {
01708         r.x = rect.x + (rect.w-surf->w)/2;
01709         r.y = rect.y + (rect.h-surf->h)/2;
01710         SDL_BlitSurface(surf, NULL, target, &r);
01711     }
01712     update_rect(rect);
01713 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs