Crossfire Client, Branch  R11627
sdl.c
Go to the documentation of this file.
00001 const char * const rcsid_gtk_sdl_c =
00002     "$Id: sdl.c 9190 2008-06-01 08:53:05Z anmaster $";
00003 
00004 /*
00005     CrossFire, A Multiplayer game for X-windows
00006 
00007     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00008     Copyright (C) 1992 Frank Tore Johansen
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019 
00020     You should have received a copy of the GNU General Public License
00021     along with this program; if not, write to the Free Software
00022     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00023 
00024     The author can be reached via e-mail to crossfire-devel@real-time.com
00025 */
00026 
00027 #include <config.h>
00028 #ifdef HAVE_SDL
00029 #include <client-types.h>
00030 #include <SDL.h>
00031 #include <SDL_image.h>
00032 
00033 /* Pick up the gtk headers we need */
00034 #include <gtk/gtk.h>
00035 #include <gdk/gdkx.h>
00036 #include <gdk/gdkkeysyms.h>
00037 
00038 #include "gx11.h"
00039 #include <client.h>
00040 #include "mapdata.h"
00041 
00042 /* Actual SDL surface the game view is painted on */
00043 SDL_Surface* mapsurface;
00044 static SDL_Surface* lightmap;
00045 static SDL_Surface* fogmap;
00046 
00047 
00048 /* Move some of the SDL code to this file here.  This makes it easier to share
00049  * between the gnome and gtk client.  It also reduces the length of both the gx11.c
00050  * and gnome.c file.  It also is more readable, as not as many #ifdef SDL.. #endif
00051  * constructs are needed.
00052  * Note that there may still be some SDL code in gx11.c - some areas are embedded
00053  * so much that it is not easy to remove.
00054  */
00055 
00056 /* these should generally be included by the file including this file. */
00057 #include <SDL.h>
00058 #include <SDL_image.h>
00059 
00060 
00061 static void do_SDL_error(const char *SDL_function, const char *file, int line)
00062 {
00063   LOG(LOG_CRITICAL,SDL_function,"SDL error in file %s line %d\n%s",
00064            file, line, SDL_GetError());
00065   SDL_Quit();
00066   exit( 1);
00067 }
00068 
00069 /*
00070  * Set the pixel at (x, y) to the given value
00071  * NOTE: The surface must be locked before calling this!
00072  * This function is directly grabbed from the SDL docs.
00073  * Note this is not currently used, but is useful enough
00074  * that it should be included.
00075  */
00076 static void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
00077 {
00078     int bpp = surface->format->BytesPerPixel;
00079     /* Here p is the address to the pixel we want to set */
00080     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
00081 
00082     switch(bpp) {
00083     case 1:
00084         *p = pixel;
00085         break;
00086 
00087     case 2:
00088         *(Uint16 *)p = pixel;
00089         break;
00090 
00091     case 3:
00092         if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
00093             p[0] = (pixel >> 16) & 0xff;
00094             p[1] = (pixel >> 8) & 0xff;
00095             p[2] = pixel & 0xff;
00096         } else {
00097             p[0] = pixel & 0xff;
00098             p[1] = (pixel >> 8) & 0xff;
00099             p[2] = (pixel >> 16) & 0xff;
00100         }
00101         break;
00102 
00103     case 4:
00104         *(Uint32 *)p = pixel;
00105         break;
00106     }
00107 }
00108 
00109 static void overlay_grid( int re_init, int ax, int ay)
00110 {
00111 
00112   static SDL_Surface* grid_overlay;
00113 
00114   static int first_pass;
00115 
00116   int x= 0;
00117   int y= 0;
00118   SDL_Rect dst;
00119   Uint32 *pixel;
00120   SDL_PixelFormat* fmt;
00121 
00122   /* Need to convert back to screen coordinates */
00123   ax-= pl_pos.x;
00124   ay-= pl_pos.y;
00125 
00126   if( re_init == TRUE)
00127     {
00128       if( grid_overlay)
00129         SDL_FreeSurface( grid_overlay);
00130 
00131       first_pass= 0;
00132       grid_overlay= NULL;
00133     }
00134 
00135   if( grid_overlay == NULL)
00136     {
00137       grid_overlay= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA,
00138                                           use_config[CONFIG_MAPWIDTH]*map_image_size,
00139                                           use_config[CONFIG_MAPHEIGHT]*map_image_size,
00140                                           mapsurface->format->BitsPerPixel,
00141                                           mapsurface->format->Rmask,
00142                                           mapsurface->format->Gmask,
00143                                           mapsurface->format->Bmask,
00144                                           mapsurface->format->Amask);
00145       if( grid_overlay == NULL)
00146         do_SDL_error( "CreateRGBSurface", __FILE__, __LINE__);
00147 
00148       grid_overlay= SDL_DisplayFormatAlpha( grid_overlay);
00149 
00150       first_pass= 0;
00151     }
00152 
00153   /*
00154    * If this is our first time drawing the grid, we need to build up the
00155    * grid overlay
00156    */
00157   if( first_pass== 0)
00158     {
00159 
00160       /* Red pixels around the edge and along image borders
00161        * fully transparent pixels everywhere else
00162        */
00163 
00164       fmt= grid_overlay->format;
00165       for( x= 0; x < map_image_size*use_config[CONFIG_MAPWIDTH]; x++)
00166         {
00167           for( y= 0; y < map_image_size*use_config[CONFIG_MAPHEIGHT]; y++)
00168             {
00169               /* FIXME: Only works for 32 bit displays right now */
00170               pixel= (Uint32*)grid_overlay->pixels+y*grid_overlay->pitch/4+x;
00171 
00172               if( x == 0 || y == 0 ||
00173                   ((x % map_image_size) == 0) || ((y % map_image_size) == 0 ) ||
00174                   y == use_config[CONFIG_MAPHEIGHT]*map_image_size-1 || x == use_config[CONFIG_MAPWIDTH]*map_image_size -1 )
00175                 {
00176                   *pixel= SDL_MapRGBA( fmt, 255, 0, 0, SDL_ALPHA_OPAQUE);
00177                 }
00178               else
00179                 {
00180                   *pixel= SDL_MapRGBA( fmt, 0, 0, 0, SDL_ALPHA_TRANSPARENT);
00181                 }
00182             }
00183         }
00184       first_pass= 1;
00185 
00186       /*
00187        * If this is our first pass then we need to overlay the entire grid
00188        * now. Otherwise we just update the tile we are on
00189        */
00190       dst.x= 0;
00191       dst.y= 0;
00192       dst.w= map_image_size*use_config[CONFIG_MAPWIDTH];
00193       dst.h= map_image_size*use_config[CONFIG_MAPHEIGHT];
00194       SDL_BlitSurface( grid_overlay, NULL, mapsurface, &dst);
00195     }
00196   else
00197     {
00198       dst.x= ax* map_image_size;
00199       dst.y= ay* map_image_size;
00200       dst.w= map_image_size;
00201       dst.h= map_image_size;
00202       /* One to one pixel mapping of grid and mapsurface so we
00203        * can share the SDL_Rect
00204        */
00205       SDL_BlitSurface( grid_overlay, &dst, mapsurface, &dst);
00206     }
00207 
00208   return;
00209 }
00210 
00211 /*
00212  * Takes two args, the first is the GtkWindow to draw on, this should always
00213  * be 'drawingarea'. The second is a boolean, if 0 then the whole
00214  * SDL system in initialized or reinited if already run once before,
00215  * if non zero then only the lightmap is rebuilt, if we switch between
00216  * per-pixel or per-tile lighting
00217  */
00218 void init_SDL( GtkWidget* sdl_window, int just_lightmap)
00219 {
00220 
00221     char SDL_windowhack[32];
00222 
00223     if( just_lightmap == 0) {
00224         g_assert( sdl_window != NULL);
00225         if( SDL_WasInit( SDL_INIT_VIDEO) != 0) {
00226             if( lightmap)
00227                 SDL_FreeSurface( lightmap);
00228             if( mapsurface)
00229                 SDL_FreeSurface( mapsurface);
00230             SDL_Quit();
00231         }
00232 
00233         /*
00234          * SDL hack to tell SDL which xwindow to paint onto
00235          */
00236         sprintf( SDL_windowhack, "SDL_WINDOWID=%ld",
00237                GDK_WINDOW_XWINDOW(sdl_window->window) );
00238         putenv( SDL_windowhack);
00239 
00240         if( SDL_Init( SDL_INIT_VIDEO) < 0)
00241         {
00242             LOG(LOG_CRITICAL,"gtk::init_SDL", "Could not initialize SDL: %s", SDL_GetError());
00243             gtk_main_quit();
00244         }
00245 
00246         mapsurface= SDL_SetVideoMode( map_image_size*use_config[CONFIG_MAPWIDTH], map_image_size*use_config[CONFIG_MAPHEIGHT], 0,
00247                                     SDL_HWSURFACE|SDL_DOUBLEBUF);
00248 
00249         if( mapsurface == NULL)
00250         {
00251             do_SDL_error( "SetVideoMode", __FILE__, __LINE__);
00252         }
00253 
00254         if( fogmap)
00255             SDL_FreeSurface( fogmap);
00256 
00257         fogmap= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA, map_image_size,
00258                                 map_image_size,
00259                                 mapsurface->format->BitsPerPixel,
00260                                 mapsurface->format->Rmask,
00261                                 mapsurface->format->Gmask,
00262                                 mapsurface->format->Bmask,
00263                                 mapsurface->format->Amask);
00264 
00265         if( fogmap == NULL)
00266         {
00267             do_SDL_error( "SDL_CreateRGBSurface", __FILE__, __LINE__);
00268         }
00269 
00270         /*
00271          * This is a persurface alpha value, not an alpha channel value.
00272          * So this surface doesn't actually need a full alpha channel
00273          */
00274         if( SDL_SetAlpha( fogmap, SDL_SRCALPHA|SDL_RLEACCEL, 128) < 0)
00275         {
00276             do_SDL_error( "SDL_SetAlpha", __FILE__, __LINE__);
00277         }
00278     }
00279 
00280     if( just_lightmap != 0 && lightmap)
00281         SDL_FreeSurface( lightmap);
00282 
00283     lightmap= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA, map_image_size,
00284                                   map_image_size,
00285                                   mapsurface->format->BitsPerPixel,
00286                                   mapsurface->format->Rmask,
00287                                   mapsurface->format->Gmask,
00288                                   mapsurface->format->Bmask,
00289                                   mapsurface->format->Amask);
00290     if( lightmap == NULL)
00291     {
00292         do_SDL_error( "SDL_CreateRGBSurface", __FILE__, __LINE__);
00293     }
00294 
00295     if(use_config[CONFIG_LIGHTING] != CFG_LT_TILE)
00296     {
00297         /* Convert surface to have a full alpha channel if we are doing
00298          * per-pixel lighting */
00299         lightmap= SDL_DisplayFormatAlpha( lightmap);
00300         if( lightmap == NULL)
00301         {
00302             do_SDL_error( "DisplayFormatAlpha", __FILE__, __LINE__);
00303         }
00304     }
00305 
00306     if(use_config[CONFIG_SHOWGRID] == TRUE)
00307     {
00308         overlay_grid( TRUE, 0, 0);
00309     }
00310 }
00311 
00312 
00313 /* Draw a alpha square on lightmap. Topleft corner is at startx,starty.
00314  * values for topleft, topright, bottomleft,bottomright corners are knowns
00315  * This use bilinear interpolation for other points. Width and heights are given
00316  * for surrouding known values square. Interpolation is done in a small square whose
00317  * coordinates are given by start{x|y} and end{x|y}
00318  * dest{x|y} is top-left corner in destination map.
00319  *                             Tchize 22 May 2004
00320  */
00321 
00322 static void drawquarterlightmap_sdl(int tl, int tr, int bl, int br,         /*colors*/
00323                              int width, int height,                         /*color square size*/
00324                              int startx, int starty, int endx, int endy,    /*interpolation region*/
00325                              int destx, int desty){                         /*where in lightmap to save result*/
00326         int x,y;
00327         int top,bottom,val;
00328         for (x=startx;x<endx;x++){
00329                 top= ((x*(tr-tl))/ width)+tl;    /*linear interpolation for top color*/
00330                 bottom= ((x*(br-bl))/ width)+bl;  /*linear interpolation for bottom color*/
00331                 for (y=starty;y<endy;y++){
00332                         val=((y*(bottom-top))/height)+top; /*linear interpolation between top and bottom*/
00333                         if (val>255)
00334                                 val=255;
00335                         if (val<0)
00336                                 val=0;
00337                         /*printf("writing pel at %d,%d\n",destx+x,desty+y);*/
00338                         putpixel(lightmap, destx+x-startx, desty+y-starty,
00339                                 SDL_MapRGBA(lightmap->format, 0, 0, 0, val));
00340                 }
00341         }
00342 }
00343 
00344 /* Do the lighting on a per pixel basis.
00345  * x and y are coordinates on the drawable map surfaces (but in terms of
00346  * spaces, not pixels).  mx and my are indexes into the
00347  * the_map.cells[][] array.
00348  * All the below goes out and figures lighting for each pixel on
00349  * the space, and creates a surface (with alpha) that is then put on
00350  * top of the exiting map space.
00351  *
00352  * TODO: I think it is possible to greatly speed this up by using
00353  * pre-generated darkness masks.  Doing all the possibilities
00354  * would be 3125 images (5 positions, each with 5 values, 5^5),
00355  * Doing it based on quadrants would only reduce that to 1024.
00356  * But I _think_ it may be possible to do this with just 64 images
00357  * (2^5 + one 90 degree rotation of the same) based on quadrants.
00358  * ie, do a 16x16 image with the 5 gradiants (0,64,128,255 at the
00359  * left, and each of those values at the right).  Then do the same
00360  * idea for top and bottom.  For any single quadrant, you would
00361  * then merge thse two values (which can be done with a fast blit),
00362  * corresponding to the right values, and you do the same thing for
00363  * the other four quadrants.  Note this only works so long as
00364  * 4 lighting values are used - if more are added, this quickly
00365  * breaks.  Also, if lighting colored effects are desired,
00366  * this also doesn't work very well.
00367  *
00368  * For now, I've just kept the old logic. MSW 2001-10-09
00369  */
00370 
00371 /* See note below about ALPHA_FUDGE - used to adjust lighting effects some */
00372 
00373 #define ALPHA_FUDGE(x)  (2*(x) / 3)
00374 static void do_sdl_per_pixel_lighting(int x, int y, int mx, int my)
00375 {
00376     int dark0, dark1, dark2, dark3, dark4;
00377     SDL_Rect dst;
00378 
00379     /* I use dark0 -> dark4 in the order to keep it similar to
00380      * the old code.
00381      */
00382     dark0 = the_map.cells[mx][my].darkness;
00383 
00384     if (y-1 < 0 || !the_map.cells[mx][my-1].have_darkness) dark1 = dark0;
00385     else dark1 = the_map.cells[mx][my-1].darkness;
00386 
00387     if (x+1 >= use_config[CONFIG_MAPWIDTH] || !the_map.cells[mx+1][my].have_darkness) dark2 = dark0;
00388     else dark2 = the_map.cells[mx+1][my].darkness;
00389 
00390     if (y+1 >= use_config[CONFIG_MAPHEIGHT] || !the_map.cells[mx][my+1].have_darkness) dark3 = dark0;
00391     else dark3 = the_map.cells[mx][my+1].darkness;
00392 
00393     if (x-1 < 0 || !the_map.cells[mx-1][my].have_darkness) dark4 = dark0;
00394     else dark4 = the_map.cells[mx-1][my].darkness;
00395 
00396     /* If they are all the same, processing is easy
00397      *
00398      * Note, the best lightining algorithm also uses diagonals
00399      * so we should check the diagonals are same too
00400      * We don't check for now, simply do all raw computation on best mode
00401      * Tchize 19 may 2004
00402      */
00403     if (dark0 == dark1 && dark0 == dark2 && dark0 == dark3 && dark0 == dark4 && (use_config[CONFIG_LIGHTING] != CFG_LT_PIXEL_BEST)) {
00404         dst.x = x * map_image_size;
00405         dst.y = y * map_image_size;
00406         dst.w = map_image_size;
00407         dst.h = map_image_size;
00408 
00409         if (dark0 == 255) {
00410             SDL_FillRect(mapsurface,&dst, SDL_MapRGB(mapsurface->format, 0, 0, 0));
00411         } else if (the_map.cells[mx][my].darkness != 0) {
00412             SDL_FillRect(lightmap,NULL, SDL_MapRGBA(lightmap->format, 0, 0, 0, the_map.cells[mx][my].darkness));
00413             SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
00414         }
00415         return;
00416     }
00417 
00418 
00419     if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL ) {
00420         /* This almost works as well as the per pixel code below, but does have some various
00421          * artifacts in the drawing.  It uses the same logic as the per pixel code below,
00422          * bit since SDL does the blit, the alpha handling ends up being different
00423          * (I think it ends up being additive).  This results in the darkness being
00424          * darker, but you also don't get the smooth effects.  If you divide all the values
00425          * by 2 (change ALPHA_FUDGE), the blending is smooth, but now the things are not dark
00426          * enough, so the blending aganst solid black spaces does not look good.
00427          * The reason this code is of interest is that on my system, it is about 50%
00428          * faster than the code below (25 ms to darkness the church in the starting
00429          * town vs 50 ms for the code further down)
00430          * Setting ALPHA_FUDGE to 2/3 seems to reduce the artifacts described above
00431          * to fairly minimal levels, while still keeping things dark enough.
00432          * MSW 2001-10-12
00433          */
00434 
00435         int i;
00436 
00437         if (dark1 == dark0) {
00438             /* If we don't have usable darkness at the top, then this entire region
00439             * should be the same value.  Likewise, if the top value and center value
00440             * are the same, we can do the entire region.
00441             */
00442             dst.x=0;
00443             dst.y=0;
00444             dst.w = map_image_size;
00445             dst.h = map_image_half_size;
00446             SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0)));
00447         }
00448         else for (i=0; i<map_image_half_size; i++) {
00449             /* Need to do it line by line */
00450 
00451             dst.x = 0;
00452             dst.y = i;
00453             dst.w = map_image_size;
00454             dst.h = 1;
00455             SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0,
00456                         ALPHA_FUDGE((map_image_half_size - i) * dark1 + i * dark0)/map_image_half_size));
00457 
00458         }
00459         /* All the following blocks are basically the same as above, just different
00460          * darkness areas.
00461          */
00462         if (dark3 == dark0) {
00463             dst.x=0;
00464             dst.y=map_image_half_size;
00465             dst.w = map_image_size;
00466             dst.h = map_image_half_size;
00467             SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0)));
00468         }
00469         else for (i=map_image_half_size; i<map_image_size; i++) {
00470             /* Need to do it line by line */
00471 
00472             dst.x = 0;
00473             dst.y = i;
00474             dst.w = map_image_size;
00475             dst.h = 1;
00476             SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0,
00477                         ALPHA_FUDGE(dark0*(map_image_size-i) + dark3*(i-map_image_half_size)) / map_image_half_size));
00478         }
00479         /* Blit this to the screen now.  Otherwise, we need to look at the alpha values
00480          * and re-average.
00481          */
00482 
00483         dst.x= x * map_image_size;
00484         dst.y= y * map_image_size;
00485         SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
00486 
00487         if (dark4 == dark0) {
00488             dst.x=0;
00489             dst.y=0;
00490             dst.w = map_image_half_size;
00491             dst.h = map_image_size;
00492             SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0)));
00493         }
00494         else for (i=0; i<map_image_half_size; i++) {
00495             /* Need to do it line by line */
00496             dst.x = i;
00497             dst.y = 0;
00498             dst.w = 1;
00499             dst.h = map_image_size;
00500             SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0,
00501                         ALPHA_FUDGE(dark4*(map_image_half_size-i) + dark0*i) / map_image_half_size));
00502         }
00503         if (dark2 == dark0) {
00504             dst.x=map_image_half_size;
00505             dst.y=0;
00506             dst.w = map_image_half_size;
00507             dst.h = map_image_size;
00508             SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0)));
00509         }
00510         else for (i=map_image_half_size; i<map_image_size; i++) {
00511             /* Need to do it line by line */
00512 
00513             dst.x = i;
00514             dst.y = 0;
00515             dst.w = 1;
00516             dst.h = map_image_size;
00517             SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0,
00518                         ALPHA_FUDGE(dark0*(map_image_size-i) + dark2*(i-map_image_half_size)) / map_image_half_size));
00519         }
00520         dst.x= x * map_image_size;
00521         dst.y= y * map_image_size;
00522         SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
00523     } else if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST ) {
00524 #if 0
00525         int dx,dy;
00526         static  int *darkx=NULL, *darky=NULL,darkx_allocated=0;
00527 
00528         /* Generated stored for the darkx[] array.  Do it dynamically, but
00529          * only allocate if the size needs to be expanded to keep performance
00530          * better.  darkx could be null in the initial case, but realloc should
00531          * just treat that as a malloc (so according to the man page)
00532          */
00533         if (map_image_size > darkx_allocated) {
00534             darkx = realloc(darkx, map_image_size * sizeof(int));
00535             darky = realloc(darky, map_image_size * sizeof(int));
00536             darkx_allocated = map_image_size;
00537         }
00538 
00539         for( dx= 0; dx < map_image_half_size; dx++)
00540             darkx[dx]= (dark4*(map_image_half_size-dx) + dark0*dx) / map_image_half_size;
00541         for( dx= map_image_half_size; dx < map_image_size; dx++)
00542             darkx[dx] = (dark0*(map_image_size-dx) + dark2*(dx-map_image_half_size)) / map_image_half_size;
00543 
00544         for( dy= 0; dy < map_image_half_size; dy++)
00545             darky[dy]= (dark1*(map_image_half_size-dy) + dark0*dy) / map_image_half_size;
00546         for( dy= map_image_half_size; dy < map_image_size; dy++)
00547             darky[dy] = (dark0*(map_image_size-dy) + dark3*(dy-map_image_half_size)) / map_image_half_size;
00548 
00549         SDL_LockSurface( lightmap);
00550 
00551         for (dx=0; dx<map_image_size; dx++)
00552             for (dy=0; dy<map_image_size; dy++)
00553                 putpixel(lightmap, dx, dy, SDL_MapRGBA(lightmap->format, 0, 0, 0,(darkx[dx] + darky[dy])/2));
00554 #else
00555         /*we need additionnal surrounding infos*/
00556         int dark5, dark6, dark7, dark8;
00557         if ( (y-1 < 0) || (x+1 >= use_config[CONFIG_MAPWIDTH])
00558                 || !the_map.cells[mx+1][my-1].have_darkness) dark5 = (dark1+dark2)>>1; /*(fast div 2)*/
00559         else dark5 = the_map.cells[mx+1][my-1].darkness;
00560 
00561         if ( (x+1 >= use_config[CONFIG_MAPWIDTH])
00562                 || (y+1 >= use_config[CONFIG_MAPHEIGHT])
00563                 || !the_map.cells[mx+1][my+1].have_darkness) dark6 = (dark2+dark3)>>1;
00564         else dark6 = the_map.cells[mx+1][my+1].darkness;
00565 
00566         if ( (y+1 >= use_config[CONFIG_MAPHEIGHT]) || (x-1 < 0)
00567                 || !the_map.cells[mx-1][my+1].have_darkness) dark7 = (dark3+dark4)>>1;
00568         else dark7 = the_map.cells[mx-1][my+1].darkness;
00569 
00570         if ( (x-1 < 0) || (y-1 < 0)
00571                 || !the_map.cells[mx-1][my-1].have_darkness) dark8 = (dark4+dark1)>>1;
00572         else dark8 = the_map.cells[mx-1][my-1].darkness;
00573         /*upper left lightmap quarter*/
00574         drawquarterlightmap_sdl(dark8, dark1, dark4, dark0,                /*colors*/
00575                              map_image_size, map_image_size,               /*color square size*/
00576                              map_image_half_size, map_image_half_size, map_image_size, map_image_size,    /*interpolation region*/
00577                              0, 0);                         /*where in lightmap to save result*/
00578         /*upper right lightmap quarter*/
00579         drawquarterlightmap_sdl(dark1, dark5, dark0, dark2,                /*colors*/
00580                              map_image_size, map_image_size,               /*color square size*/
00581                              0, map_image_half_size, map_image_half_size, map_image_size,    /*interpolation region*/
00582                              map_image_half_size, 0);                         /*where in lightmap to save result*/
00583         /*bottom left lightmap quarter*/
00584         drawquarterlightmap_sdl(dark4, dark0, dark7, dark3,                /*colors*/
00585                              map_image_size, map_image_size,               /*color square size*/
00586                              map_image_half_size, 0, map_image_size, map_image_half_size,    /*interpolation region*/
00587                              0, map_image_half_size);                         /*where in lightmap to save result*/
00588         /*bottom right lightmap quarter*/
00589         drawquarterlightmap_sdl(dark0, dark2, dark3, dark6,                /*colors*/
00590                              map_image_size, map_image_size,               /*color square size*/
00591                              0, 0, map_image_half_size, map_image_half_size,    /*interpolation region*/
00592                              map_image_half_size, map_image_half_size);                         /*where in lightmap to save result*/
00593 #endif
00594         dst.w= map_image_size;
00595         dst.h= map_image_size;
00596         dst.x= x * map_image_size;
00597         dst.y= y * map_image_size;
00598         SDL_UnlockSurface(lightmap);
00599         SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
00600     }
00601 }
00602 /* Draw anything in adjacent squares that could smooth on given square
00603  * mx,my square to smooth on. you should not call this function to
00604  * smooth on a 'completly black' square. (simply for visual result)
00605  * layer layer to examine (we smooth only one layer at a time)
00606  * dst place on the mapwindow to draw
00607  */
00608 static void drawsmooth_sdl (int mx,int my,int layer,SDL_Rect dst){
00609     static int dx[8]={0,1,1,1,0,-1,-1,-1};
00610     static int dy[8]={-1,-1,0,1,1,1,0,-1};
00611     static int bweights[8]={2,0,4,0,8,0,1,0};
00612     static int cweights[8]={0,2,0,4,0,8,0,1};
00613     static int bc_exclude[8]={
00614                  1+2,/*north exclude northwest (bit0) and northeast(bit1)*/
00615                  0,
00616                  2+4,/*east exclude northeast and southeast*/
00617                  0,
00618                  4+8,/*and so on*/
00619                  0,
00620                  8+1,
00621                  0
00622                 };
00623     int partdone[8]={0,0,0,0,0,0,0,0};
00624     int slevels[8];
00625     int sfaces[8];
00626     int i,lowest,weight,weightC;
00627     int emx,emy;
00628     int smoothface;
00629     int hasFace = 0;
00630     SDL_Rect src;
00631     for (i=0;i<=layer;i++)
00632         hasFace |= the_map.cells[mx][my].heads[i].face;
00633     if (!hasFace
00634     || !CAN_SMOOTH(the_map.cells[mx][my], layer)) {
00635         return;
00636     }
00637 
00638     src.w = dst.w;
00639     src.h = dst.h;
00640 
00641     for (i=0;i<8;i++){
00642         emx=mx+dx[i];
00643         emy=my+dy[i];
00644         if ( (emx<0) || (emy<0) || (the_map.x<=emx) || (the_map.y<=emy)){
00645             slevels[i]=0;
00646             sfaces[i]=0; /*black picture*/
00647         }
00648         else if (the_map.cells[emx][emy].smooth[layer]<=the_map.cells[mx][my].smooth[layer]){
00649             slevels[i]=0;
00650             sfaces[i]=0; /*black picture*/
00651         }else{
00652             slevels[i]=the_map.cells[emx][emy].smooth[layer];
00653             sfaces[i]=pixmaps[the_map.cells[emx][emy].heads[layer].face]->smooth_face;
00654         }
00655     }
00656     /* ok, now we have a list of smoothlevel higher than current square.
00657      * there are at most 8 different levels. so... let's check 8 times
00658      * for the lowest one (we draw from botto to top!).
00659      */
00660     lowest=-1;
00661     while (1){
00662         lowest = -1;
00663         for (i=0;i<8;i++){
00664             if ( (slevels[i]>0) && (!partdone[i]) &&
00665                 ((lowest<0) || (slevels[i]<slevels[lowest]))
00666                )
00667                     lowest=i;
00668         }
00669         if (lowest<0)
00670             break;   /*no more smooth to do on this square*/
00671         /*printf ("hey, must smooth something...%d\n",sfaces[lowest]);*/
00672         /*here we know 'what' to smooth*/
00673         /* we need to calculate the weight
00674          * for border and weight for corners.
00675          * then we 'markdone'
00676          * the corresponding squares
00677          */
00678         /*first, the border, which may exclude some corners*/
00679         weight=0;
00680         weightC=15; /*works in backward. remove where there is nothing*/
00681         /*for (i=0;i<8;i++)
00682             cornermask[i]=1;*/
00683         for (i=0;i<8;i++){ /*check all nearby squares*/
00684             if ( (slevels[i]==slevels[lowest]) &&
00685                  (sfaces[i]==sfaces[lowest])){
00686                 partdone[i]=1;
00687                 weight=weight+bweights[i];
00688                 weightC&=~bc_exclude[i];
00689             }else{
00690                 /*must rmove the weight of a corner if not in smoothing*/
00691                 weightC&=~cweights[i];
00692             }
00693 
00694         }
00695         /*We can't do this before since we need the partdone to be adjusted*/
00696         if (sfaces[lowest]<=0)
00697             continue;  /*Can't smooth black*/
00698         smoothface=sfaces[lowest];
00699         if (smoothface<=0){
00700             continue;  /*picture for smoothing not yet available*/
00701         }
00702         /* now, it's quite easy. We must draw using a 32x32 part of
00703          * the picture smoothface.
00704          * This part is located using the 2 weights calculated:
00705          * (32*weight,0) and (32*weightC,32)
00706          */
00707         if ( (!pixmaps[smoothface]->map_image) ||
00708              (pixmaps[smoothface] == pixmaps[0]))
00709             continue;   /*don't have the picture associated*/
00710         if (weight>0){
00711             src.x=map_image_size*weight;
00712             src.y=0;
00713             if (the_map.cells[mx][my].cleared) {
00714                 if (SDL_BlitSurface(pixmaps[smoothface]->fog_image,
00715                         &src, mapsurface, &dst))
00716                     do_SDL_error( "BlitSurface", __FILE__, __LINE__);
00717             } else {
00718                 if (SDL_BlitSurface(pixmaps[smoothface]->map_image,
00719                         &src, mapsurface, &dst))
00720                     do_SDL_error( "BlitSurface", __FILE__, __LINE__);
00721             }
00722         }
00723         if (weightC>0){
00724             src.x=map_image_size*weightC;
00725             src.y=map_image_size;
00726             if (the_map.cells[mx][my].cleared) {
00727                 if (SDL_BlitSurface(pixmaps[smoothface]->fog_image,
00728                         &src, mapsurface, &dst))
00729                     do_SDL_error( "BlitSurface", __FILE__, __LINE__);
00730             } else {
00731                 if (SDL_BlitSurface(pixmaps[smoothface]->map_image,
00732                         &src, mapsurface, &dst))
00733                     do_SDL_error( "BlitSurface", __FILE__, __LINE__);
00734             }
00735         }
00736     }/*while there's some smooth to do*/
00737 }
00738 
00739 /* This function tells if a specifi square need to be redrawn
00740  * Reason for redrawing can be content change, smoothing change or
00741  * surrounding lightning change. Conditions depend on type of
00742  * lightning code used.
00743  *                              Tchize 22 May 2004
00744  */
00745 static int sdl_square_need_redraw(int mx, int my){
00746 #define SDL_LIGHT_CHANGED(_x_,_y_) (!mapdata_is_inside((_x_), (_y_)) ? 0 : the_map.cells[_x_][_y_].need_update)
00747     if ( (the_map.cells[mx][my].need_update) || (the_map.cells[mx][my].need_resmooth))
00748         return 1;
00749 
00750     if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL){
00751         /*The fast per pixel uses 4 additionnal datas which may have changed:*/
00752         /*we suppose need_redraw -> lightr may have change. in future maybe we could add a need_relight toggle*/
00753         if (SDL_LIGHT_CHANGED(mx-1,my) ||
00754             SDL_LIGHT_CHANGED(mx,my-1) ||
00755             SDL_LIGHT_CHANGED(mx+1,my) ||
00756             SDL_LIGHT_CHANGED(mx,my+1))
00757             return 1;
00758     }
00759     if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST){
00760         if (SDL_LIGHT_CHANGED(mx-1,my) ||
00761             SDL_LIGHT_CHANGED(mx,my-1) ||
00762             SDL_LIGHT_CHANGED(mx+1,my) ||
00763             SDL_LIGHT_CHANGED(mx,my+1) ||
00764             SDL_LIGHT_CHANGED(mx-1,my+1) ||
00765             SDL_LIGHT_CHANGED(mx-1,my-1) ||
00766             SDL_LIGHT_CHANGED(mx+1,my-1) ||
00767             SDL_LIGHT_CHANGED(mx+1,my+1) )
00768             return 1;
00769     }
00770     return 0; /*no need to redraw :)*/
00771 }
00772 
00773 static void display_mapcell(int ax, int ay, int mx, int my)
00774 {
00775     SDL_Rect dst, src;
00776     int layer;
00777 
00778     /* First, we need to black out this space. */
00779     dst.x = ax*map_image_size;
00780     dst.y = ay*map_image_size;
00781     dst.w = map_image_size;
00782     dst.h = map_image_size;
00783     SDL_FillRect(mapsurface, &dst, SDL_MapRGB(mapsurface->format, 0, 0, 0));
00784 
00785     /* now draw the different layers.  Only draw if using fog of war or the
00786      * space isn't clear.
00787      */
00788     if (use_config[CONFIG_FOGWAR] || !the_map.cells[mx][my].cleared) {
00789         for (layer=0; layer<MAXLAYERS; layer++) {
00790             int sx, sy;
00791 
00792             /* draw single-tile faces first */
00793             int face = mapdata_face(ax, ay, layer);
00794             if (face > 0 && pixmaps[face]->map_image != NULL) {
00795                 int w = pixmaps[face]->map_width;
00796                 int h = pixmaps[face]->map_height;
00797                 /* add one to the size values to take into account the actual width of the space */
00798                 src.x = w-map_image_size;
00799                 src.y = h-map_image_size;
00800                 src.w = map_image_size;
00801                 src.h = map_image_size;
00802                 dst.x = ax*map_image_size;
00803                 dst.y = ay*map_image_size;
00804                 if (the_map.cells[mx][my].cleared) {
00805                     if (SDL_BlitSurface(pixmaps[face]->fog_image, &src, mapsurface, &dst))
00806                         do_SDL_error( "BlitSurface", __FILE__, __LINE__);
00807                 } else {
00808                     if (SDL_BlitSurface(pixmaps[face]->map_image, &src, mapsurface, &dst))
00809                         do_SDL_error( "BlitSurface", __FILE__, __LINE__);
00810                 }
00811 
00812             }
00813             /* Sometimes, it may happens we need to draw the smooth while there
00814              * is nothing to draw at that layer (but there was something at lower
00815              * layers). This is handled here. The else part is to take into account
00816              * cases where the smooth as already been handled 2 code lines before
00817              */
00818             if (use_config[CONFIG_SMOOTH])
00819                 drawsmooth_sdl (mx,my,layer,dst);
00820 
00821            /* draw big faces last (should overlap other objects) */
00822             face = mapdata_bigface(ax, ay, layer, &sx, &sy);
00823             if (face > 0 && pixmaps[face]->map_image != NULL) {
00824                 /* We have to handle images that are not an equal
00825                  * multiplier of map_image_size.  See
00826                  * display_mapcell() in gtk-v2/src/map.c for
00827                  * more details on this logic, since it is basically
00828                  * the same.
00829                  */
00830                 int dx, dy, sourcex, sourcey, offx, offy;
00831 
00832                 dx = pixmaps[face]->map_width % map_image_size;
00833                 offx = dx?(map_image_size -dx):0;
00834 
00835                 if (sx) {
00836                     sourcex = sx * map_image_size - offx ;
00837                     offx=0;
00838                 } else {
00839                     sourcex=0;
00840                 }
00841 
00842                 dy = pixmaps[face]->map_height % map_image_size;
00843                 offy = dy?(map_image_size -dy):0;
00844 
00845                 if (sy) {
00846                     sourcey = sy * map_image_size - offy;
00847                     offy=0;
00848                 } else {
00849                     sourcey=0;
00850                 }
00851 
00852                 src.x = sourcex;
00853                 src.y = sourcey;
00854                 src.w = map_image_size - offx;
00855                 src.h = map_image_size - offy;
00856                 dst.x = ax*map_image_size + offx;
00857                 dst.y = ay*map_image_size + offy;
00858 
00859                 if (the_map.cells[mx][my].cleared) {
00860                     if (SDL_BlitSurface(pixmaps[face]->fog_image, &src, mapsurface, &dst))
00861                         do_SDL_error( "BlitSurface", __FILE__, __LINE__);
00862                 } else {
00863                     if (SDL_BlitSurface(pixmaps[face]->map_image, &src, mapsurface, &dst))
00864                         do_SDL_error( "BlitSurface", __FILE__, __LINE__);
00865                 }
00866             } /* else for processing the layers */
00867         }
00868     }
00869 
00870     if (use_config[CONFIG_LIGHTING] == CFG_LT_TILE) {
00871         dst.x = ax*map_image_size;
00872         dst.y = ay*map_image_size;
00873         dst.w = map_image_size;
00874         dst.h = map_image_size;
00875 
00876         /* Note - Instead of using a lightmap, I just fillrect
00877          * directly onto the map surface - I would think this should be
00878          * faster
00879          */
00880         if (the_map.cells[mx][my].darkness == 255) {
00881             SDL_FillRect(mapsurface,&dst, SDL_MapRGB(mapsurface->format, 0, 0, 0));
00882         } else if (the_map.cells[mx][my].darkness != 0) {
00883             SDL_SetAlpha(lightmap, SDL_SRCALPHA|SDL_RLEACCEL, the_map.cells[mx][my].darkness);
00884             SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
00885         }
00886     } else if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL || use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST) {
00887         do_sdl_per_pixel_lighting(ax, ay, mx, my);
00888     }
00889 }
00890 
00891 /* This generates a map in SDL mode.
00892  *
00893  * I had to totally change the logic on how we do this in SDL mode -
00894  * to support variable sized images, the old method of generating each
00895  * space does not work, as one space may spill over to the other.
00896  * Instead, we first blit the bottom layer, then the layer above
00897  * that, and so on.  This results in the map being drawn a bit
00898  * more correctly.  In fact, that logic actually isn't needed, as
00899  * with the new map commands, we know the offset and size of the
00900  * images.
00901  *
00902  * The logic here only redraws spaces that change.  The logic in the
00903  * common/commands.c files the odd layers with links for 'big images'.
00904  * for objects on these layers, we look at the size_x and size_y values
00905  * to determine the offset from which we should be blitting.
00906  *
00907  * Old notes, but left in:
00908  * The performance here is very good in most cases - about 30 ms (on my system)
00909  * is used just for my flip at the bottom of the function, drawing only what
00910  * is needed generally saves a lot of time (<15 ms in most cases) compared to the
00911  * 80-120 ms usually needed on a 15x15 map.
00912  */
00913 
00914 void sdl_gen_map(int redraw) {
00915     int mx, my, x, y;
00916     struct timeval tv1, tv2,tv3;
00917     long elapsed1, elapsed2;
00918 
00919     static int last_mapwidth=0;
00920     static int last_mapheight=0;
00921     static uint8 *redrawbitmap=NULL;
00922 
00923     if (time_map_redraw)
00924         gettimeofday(&tv1, NULL);
00925 
00926     /* In per pixel lightning code, some square may have to be
00927      * redrawn just because a surrounding square did change
00928      * However, informations on changed square do get toggle
00929      * during the redraw process. To keep track of which
00930      * squares really need redraw (instead of redrawing eveything in
00931      * per pixel lightning case), we need to save this info in a array
00932      *                             Tchize, 22 May 2004
00933      */
00934     if ( (use_config[CONFIG_MAPWIDTH] != last_mapwidth) ||
00935          (use_config[CONFIG_MAPHEIGHT] != last_mapheight) ){
00936         /* reallocate array */
00937         last_mapwidth=use_config[CONFIG_MAPWIDTH];
00938         last_mapheight=use_config[CONFIG_MAPHEIGHT];
00939         redrawbitmap=(uint8*)malloc(sizeof(uint8)*last_mapwidth*last_mapheight);
00940     }
00941     if (redrawbitmap==NULL){
00942         LOG(LOG_ERROR,"sdl::sdl_gen_map",
00943                 "The redraw bitmap is NULL. Not enough memory? (width:%d,height:%d)",
00944                 last_mapwidth,last_mapheight);
00945         return; /*without this bitmap, no drawing possible*/
00946     }
00947 
00948     for( x= 0; x<use_config[CONFIG_MAPWIDTH]; x++) {
00949         for(y = 0; y<use_config[CONFIG_MAPHEIGHT]; y++) {
00950             redrawbitmap[x+y*last_mapwidth]=(uint8)sdl_square_need_redraw(x+pl_pos.x,y+pl_pos.y);
00951         }
00952     }
00953     for( x= 0; x<use_config[CONFIG_MAPWIDTH]; x++) {
00954         for(y = 0; y<use_config[CONFIG_MAPHEIGHT]; y++) {
00955             /* mx,my represent the spaces on the 'virtual' map (ie, the_map structure).
00956              * x and y (from the for loop) represent the visable screen.
00957              */
00958             mx = pl_pos.x+x;
00959             my = pl_pos.y+y;
00960             if (redraw || redrawbitmap[x+y*last_mapwidth]) {
00961                 display_mapcell(x, y, mx, my);
00962                 the_map.cells[mx][my].need_update = 0;
00963                 the_map.cells[mx][my].need_resmooth = 0;
00964             }
00965         }
00966     }
00967 
00968     if (time_map_redraw)
00969         gettimeofday(&tv2, NULL);
00970 
00971     SDL_Flip(mapsurface);
00972 
00973     if (time_map_redraw) {
00974         gettimeofday(&tv3, NULL);
00975         elapsed1 = (tv2.tv_sec - tv1.tv_sec)*1000000 + (tv2.tv_usec - tv1.tv_usec);
00976         elapsed2 = (tv3.tv_sec - tv2.tv_sec)*1000000 + (tv3.tv_usec - tv2.tv_usec);
00977 
00978         /* I care about performance for 'long' updates, so put the check in to make
00979          * these a little more noticable */
00980         if ((elapsed1 + elapsed2)>10000)
00981             LOG(LOG_INFO,"gtk::sdl_gen_map","gen took %7ld, flip took %7ld, total = %7ld",
00982                     elapsed1, elapsed2, elapsed1 + elapsed2);
00983     }
00984 } /* sdl_gen_map function */
00985 
00986 int sdl_mapscroll(int dx, int dy)
00987 {
00988     /* a copy of what pngximage does except sdl specfic
00989      * mapsurface->pitch is the length of a scanline in bytes
00990      * including alignment padding
00991      */
00992     SDL_LockSurface( mapsurface);
00993     if( dy < 0) {
00994         int offset= mapsurface->pitch * (-dy*map_image_size);
00995         memmove( mapsurface->pixels + offset, mapsurface->pixels,
00996                      mapsurface->pitch * (mapsurface->h + dy*map_image_size) );
00997     }
00998     else if( dy > 0) {
00999         int offset= mapsurface->pitch * (dy*map_image_size);
01000         memmove( mapsurface->pixels,  mapsurface->pixels + offset,
01001                      mapsurface->pitch * (mapsurface->h - dy*map_image_size) );
01002     }
01003 
01004     if (dx) {
01005         int y;
01006         for( y= 0; y < mapsurface->h; y++) {
01007             if( dx < 0) {
01008                 char* start_of_row= mapsurface->pixels + mapsurface->pitch * y;
01009                 int offset= ( mapsurface->format->BytesPerPixel * map_image_size * -dx);
01010                 memmove( start_of_row + offset, start_of_row,
01011                              mapsurface->pitch - offset);
01012             }
01013             else {
01014                 char* start_of_row= mapsurface->pixels + mapsurface->pitch * y;
01015                 int offset= ( mapsurface->format->BytesPerPixel * map_image_size * dx);
01016                 memmove( start_of_row, start_of_row + offset,
01017                              mapsurface->pitch - offset);
01018             }
01019         }
01020     }
01021     SDL_UnlockSurface( mapsurface);
01022 
01023     return 1;
01024 }
01025 
01026 #endif