/* * Crossfire -- cooperative multi-player graphical RPG and adventure game * * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team * Copyright (c) 1992 Frank Tore Johansen * * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are * welcome to redistribute it under certain conditions. For details, see the * 'LICENSE' and 'COPYING' files. * * The authors can be reached via e-mail to crossfire-devel@real-time.com */ /** * @file * Functions that implement SDL support in the GTK V2 client. */ #include "client.h" #ifdef HAVE_SDL #include #include #ifndef WIN32 #include #else #include #endif #include "image.h" #include "main.h" #include "mapdata.h" #include "gtk2proto.h" SDL_Surface* mapsurface; /**< Actual SDL surface the game view is painted on */ static SDL_Surface* lightmap; static SDL_Surface* fogmap; static char *redrawbitmap; /* Move some of the SDL code to this file here. This makes it easier to share * between the gnome and gtk client. It also reduces the length of both the * gx11.c and gnome.c file. It also is more readable, as not as many #ifdef * SDL.. #endif constructs are needed. Note that there may still be some SDL * code in gx11.c - some areas are embedded so much that it is not easy to * remove. */ static void do_SDL_error(const char *SDL_function, const char *file, int line) { LOG(LOG_CRITICAL,SDL_function,"SDL error in file %s line %d\n%s", file, line, SDL_GetError()); SDL_Quit(); exit( 1); } /** * Set the pixel at (x, y) to the given value * NOTE: The surface must be locked before calling this! * This function is directly grabbed from the SDL docs. * Note this is not currently used, but is useful enough * that it should be included. */ static void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel) { int bpp = surface->format->BytesPerPixel; /* Here p is the address to the pixel we want to set */ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; switch(bpp) { case 1: *p = pixel; break; case 2: *(Uint16 *)p = pixel; break; case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { p[0] = (pixel >> 16) & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = pixel & 0xff; } else { p[0] = pixel & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = (pixel >> 16) & 0xff; } break; case 4: *(Uint32 *)p = pixel; break; } } /** * * @param re_init * @param ax * @param ay */ static void overlay_grid( int re_init, int ax, int ay) { static SDL_Surface* grid_overlay; static int first_pass; int x= 0; int y= 0; SDL_Rect dst; Uint32 *pixel; SDL_PixelFormat* fmt; /* Need to convert back to screen coordinates */ ax-= pl_pos.x; ay-= pl_pos.y; if( re_init == TRUE) { if( grid_overlay) { SDL_FreeSurface( grid_overlay); } first_pass= 0; grid_overlay= NULL; } if( grid_overlay == NULL) { grid_overlay= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA, use_config[CONFIG_MAPWIDTH]*map_image_size * use_config[CONFIG_MAPSCALE] / 100, use_config[CONFIG_MAPHEIGHT]*map_image_size * use_config[CONFIG_MAPSCALE] / 100, mapsurface->format->BitsPerPixel, mapsurface->format->Rmask, mapsurface->format->Gmask, mapsurface->format->Bmask, mapsurface->format->Amask); if( grid_overlay == NULL) { do_SDL_error( "CreateRGBSurface", __FILE__, __LINE__); } grid_overlay= SDL_DisplayFormatAlpha( grid_overlay); first_pass= 0; } /* * If this is our first time drawing the grid, we need to build up the * grid overlay */ if( first_pass== 0) { /* Red pixels around the edge and along image borders * fully transparent pixels everywhere else */ fmt= grid_overlay->format; for( x= 0; x < map_image_size*use_config[CONFIG_MAPWIDTH] * use_config[CONFIG_MAPSCALE] / 100; x++) { for( y= 0; y < map_image_size*use_config[CONFIG_MAPHEIGHT] * use_config[CONFIG_MAPSCALE] / 100; y++) { /* FIXME: Only works for 32 bit displays right now */ pixel= (Uint32*)grid_overlay->pixels+y*grid_overlay->pitch/4+x; if( x == 0 || y == 0 || ((x % map_image_size * use_config[CONFIG_MAPSCALE] / 100) == 0) || ((y % map_image_size * use_config[CONFIG_MAPSCALE] / 100) == 0 ) || y == use_config[CONFIG_MAPHEIGHT]*map_image_size * use_config[CONFIG_MAPSCALE] / 100 -1 || x == use_config[CONFIG_MAPWIDTH]*map_image_size * use_config[CONFIG_MAPSCALE] / 100 -1 ) { *pixel= SDL_MapRGBA( fmt, 255, 0, 0, SDL_ALPHA_OPAQUE); } else { *pixel= SDL_MapRGBA( fmt, 0, 0, 0, SDL_ALPHA_TRANSPARENT); } } } first_pass= 1; /* * If this is our first pass then we need to overlay the entire grid * now. Otherwise we just update the tile we are on */ dst.x= 0; dst.y= 0; dst.w= map_image_size*use_config[CONFIG_MAPWIDTH] * use_config[CONFIG_MAPSCALE] / 100; dst.h= map_image_size*use_config[CONFIG_MAPHEIGHT] * use_config[CONFIG_MAPSCALE] / 100; SDL_BlitSurface( grid_overlay, NULL, mapsurface, &dst); } else { dst.x= ax* map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.y= ay* map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.w= map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.h= map_image_size * use_config[CONFIG_MAPSCALE] / 100; /* One to one pixel mapping of grid and mapsurface so we * can share the SDL_Rect */ SDL_BlitSurface( grid_overlay, &dst, mapsurface, &dst); } return; } /** * Takes two args, the first is the GtkWindow to draw on, this should always * be 'drawingarea'. The second is a boolean, if 0 then the whole * SDL system in initialized or reinited if already run once before, * if non zero then only the lightmap is rebuilt, if we switch between * per-pixel or per-tile lighting * * @param sdl_window * @param just_lightmap */ void init_SDL( GtkWidget* sdl_window, int just_lightmap) { char SDL_windowhack[32]; if( just_lightmap == 0) { g_assert( sdl_window != NULL); if( SDL_WasInit( SDL_INIT_VIDEO) != 0) { if( lightmap) { SDL_FreeSurface( lightmap); } if( mapsurface) { SDL_FreeSurface( mapsurface); } SDL_Quit(); } /* * SDL hack to tell SDL which xwindow to paint onto */ #ifndef WIN32 snprintf(SDL_windowhack, sizeof(SDL_windowhack), "SDL_WINDOWID=%ld", GDK_WINDOW_XWINDOW(gtk_widget_get_window(sdl_window)) ); #else sprintf( SDL_windowhack, "SDL_WINDOWID=%ld", GDK_WINDOW_HWND(gtk_widget_get_window(sdl_window)) ); #endif putenv( SDL_windowhack); if( SDL_Init( SDL_INIT_VIDEO) < 0) { LOG(LOG_CRITICAL,"gtk-v2::init_SDL", "Could not initialize SDL: %s", SDL_GetError()); gtk_main_quit(); } mapsurface= SDL_SetVideoMode( map_image_size*use_config[CONFIG_MAPWIDTH]*use_config[CONFIG_MAPSCALE] / 100, map_image_size*use_config[CONFIG_MAPHEIGHT]*use_config[CONFIG_MAPSCALE] / 100, 0, SDL_HWSURFACE|SDL_DOUBLEBUF); if( mapsurface == NULL) { do_SDL_error( "SetVideoMode", __FILE__, __LINE__); } if( fogmap) { SDL_FreeSurface( fogmap); } fogmap= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA, map_image_size*use_config[CONFIG_MAPSCALE] / 100, map_image_size*use_config[CONFIG_MAPSCALE] / 100, mapsurface->format->BitsPerPixel, mapsurface->format->Rmask, mapsurface->format->Gmask, mapsurface->format->Bmask, mapsurface->format->Amask); if( fogmap == NULL) { do_SDL_error( "SDL_CreateRGBSurface", __FILE__, __LINE__); } /* * This is a persurface alpha value, not an alpha channel value. * So this surface doesn't actually need a full alpha channel */ if( SDL_SetAlpha( fogmap, SDL_SRCALPHA|SDL_RLEACCEL, 128) < 0) { do_SDL_error( "SDL_SetAlpha", __FILE__, __LINE__); } } if( just_lightmap != 0 && lightmap) { SDL_FreeSurface( lightmap); } lightmap= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA, map_image_size*use_config[CONFIG_MAPSCALE] / 100, map_image_size*use_config[CONFIG_MAPSCALE] / 100, mapsurface->format->BitsPerPixel, mapsurface->format->Rmask, mapsurface->format->Gmask, mapsurface->format->Bmask, mapsurface->format->Amask); if( lightmap == NULL) { do_SDL_error( "SDL_CreateRGBSurface", __FILE__, __LINE__); } if(use_config[CONFIG_LIGHTING] != CFG_LT_TILE) { /* Convert surface to have a full alpha channel if we are doing * per-pixel lighting */ lightmap= SDL_DisplayFormatAlpha( lightmap); if( lightmap == NULL) { do_SDL_error( "DisplayFormatAlpha", __FILE__, __LINE__); } } if(use_config[CONFIG_SHOWGRID] == TRUE) { overlay_grid( TRUE, 0, 0); } /* We make this a bit bigger than the actual map - thus, there * is a 1 space pad in all directions. This enables us * to store a value in that area without having to do checks to * see if we are at the edge of the map - doing a store vs 4 * checks is going to be much faster. */ redrawbitmap = g_malloc(sizeof(char) * (MAP_MAX_SIZE +2)* (MAP_MAX_SIZE+2)); } /** * Draw a alpha square on lightmap. Values for topleft, topright, * bottomleft,bottomright corners are knowns This use bilinear interpolation * for other points. Width and heights are given for surrounding known values * square. Interpolation is done in a small square whose coordinates are * given by start{x|y} and end{x|y} * Tchize 22 May 2004 * * Note - profile shows this is a very costly function - of a small run, * 77% of the time of the cpu time for the client was in this function. * * @param tl color * @param tr color * @param bl color * @param br color * @param width color square size * @param height color square size * @param startx Top left corner of the interpolation region. * @param starty Top left corner of the interpolation region. * @param endx Bottom right corner of the interpolation region. * @param endy Bottom right corner of the interpolation region. * @param destx Coordinate of top left corner in destination map, or where in * the lightmap to save the result. * @param desty Coordinate of top left corner in destination map, or where in * the lightmap to save the result. */ void drawquarterlightmap_sdl(int tl, int tr, int bl, int br, int width, int height, int startx, int starty, int endx, int endy, int destx, int desty) { int x,y; int top,bottom,val; for (x=startx; x255) { val=255; } if (val<0) { val=0; } /*printf("writing pel at %d,%d\n",destx+x,desty+y);*/ putpixel(lightmap, destx+x-startx, desty+y-starty, SDL_MapRGBA(lightmap->format, 0, 0, 0, val)); } } } /* See note below about ALPHA_FUDGE - used to adjust lighting effects some */ #define ALPHA_FUDGE(x) (2*(x) / 3) #define GENDARK(x,y) ( (((x)&(y) & 1) == 1)?255:0 ) /** * Do the lighting on a per pixel basis. * x and y are coordinates on the drawable map surfaces (but in terms of * spaces, not pixels). mx and my are indexes into the * the_map.cells[][] array. * All the below goes out and figures lighting for each pixel on * the space, and creates a surface (with alpha) that is then put on * top of the exiting map space. * * TODO: I think it is possible to greatly speed this up by using * pre-generated darkness masks. Doing all the possibilities * would be 3125 images (5 positions, each with 5 values, 5^5), * Doing it based on quadrants would only reduce that to 1024. * But I _think_ it may be possible to do this with just 64 images * (2^5 + one 90 degree rotation of the same) based on quadrants. * ie, do a 16x16 image with the 5 gradiants (0,64,128,255 at the * left, and each of those values at the right). Then do the same * idea for top and bottom. For any single quadrant, you would * then merge thse two values (which can be done with a fast blit), * corresponding to the right values, and you do the same thing for * the other four quadrants. Note this only works so long as * 4 lighting values are used - if more are added, this quickly * breaks. Also, if lighting colored effects are desired, * this also doesn't work very well. * * For now, I've just kept the old logic. MSW 2001-10-09 * * @param x * @param y * @param mx * @param my */ static void do_sdl_per_pixel_lighting(int x, int y, int mx, int my) { int dark0, dark1, dark2, dark3, dark4; SDL_Rect dst; /* I use dark0 -> dark4 in the order to keep it similar to * the old code. */ dark0 = mapdata_cell(mx, my)->darkness; if (y-1 < 0 || !mapdata_cell(mx, my-1)->have_darkness) { dark1 = dark0; } else { dark1 = mapdata_cell(mx, my-1)->darkness; } if (x+1 >= use_config[CONFIG_MAPWIDTH] || !mapdata_cell(mx+1, my)->have_darkness) { dark2 = dark0; } else { dark2 = mapdata_cell(mx+1, my)->darkness; } if (y+1 >= use_config[CONFIG_MAPHEIGHT] || !mapdata_cell(mx, my+1)->have_darkness) { dark3 = dark0; } else { dark3 = mapdata_cell(mx, my+1)->darkness; } if (x-1 < 0 || !mapdata_cell(mx-1, my)->have_darkness) { dark4 = dark0; } else { dark4 = mapdata_cell(mx-1, my)->darkness; } /* If they are all the same, processing is easy * * Note, the best lightining algorithm also uses diagonals * so we should check the diagonals are same too * We don't check for now, simply do all raw computation on best mode * Tchize 19 may 2004 */ if (dark0 == dark1 && dark0 == dark2 && dark0 == dark3 && dark0 == dark4 && (use_config[CONFIG_LIGHTING] != CFG_LT_PIXEL_BEST)) { dst.x = x * map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.y = y * map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.w = map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.h = map_image_size * use_config[CONFIG_MAPSCALE] / 100; if (dark0 == 255) { SDL_FillRect(mapsurface,&dst, SDL_MapRGB(mapsurface->format, 0, 0, 0)); } else if (mapdata_cell(mx, my)->darkness != 0) { SDL_FillRect(lightmap,NULL, SDL_MapRGBA(lightmap->format, 0, 0, 0, mapdata_cell(mx, my)->darkness)); SDL_BlitSurface(lightmap, NULL, mapsurface, &dst); } return; } if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL ) { /* This almost works as well as the per pixel code below, but does have some various * artifacts in the drawing. It uses the same logic as the per pixel code below, * bit since SDL does the blit, the alpha handling ends up being different * (I think it ends up being additive). This results in the darkness being * darker, but you also don't get the smooth effects. If you divide all the values * by 2 (change ALPHA_FUDGE), the blending is smooth, but now the things are not dark * enough, so the blending aganst solid black spaces does not look good. * The reason this code is of interest is that on my system, it is about 50% * faster than the code below (25 ms to darkness the church in the starting * town vs 50 ms for the code further down) * Setting ALPHA_FUDGE to 2/3 seems to reduce the artifacts described above * to fairly minimal levels, while still keeping things dark enough. * MSW 2001-10-12 */ int i; if (dark1 == dark0) { /* If we don't have usable darkness at the top, then this entire region * should be the same value. Likewise, if the top value and center value * are the same, we can do the entire region. */ dst.x=0; dst.y=0; dst.w = map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.h = map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0))); } else for (i=0; iformat, 0, 0, 0, ALPHA_FUDGE((map_image_half_size * use_config[CONFIG_MAPSCALE] / 100 - i) * dark1 + i * dark0)/(map_image_half_size * use_config[CONFIG_MAPSCALE] / 100))); } /* All the following blocks are basically the same as above, just different * darkness areas. */ if (dark3 == dark0) { dst.x=0; dst.y=map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; dst.w = map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.h = map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0))); } else for (i=map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; iformat, 0, 0, 0, ALPHA_FUDGE(dark0*(map_image_size * use_config[CONFIG_MAPSCALE] / 100 - i) + dark3*(i-map_image_half_size * use_config[CONFIG_MAPSCALE] / 100)) / (map_image_half_size * use_config[CONFIG_MAPSCALE] / 100))); } /* Blit this to the screen now. Otherwise, we need to look at the alpha values * and re-average. */ dst.x= x * map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.y= y * map_image_size * use_config[CONFIG_MAPSCALE] / 100; SDL_BlitSurface(lightmap, NULL, mapsurface, &dst); if (dark4 == dark0) { dst.x=0; dst.y=0; dst.w = map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; dst.h = map_image_size * use_config[CONFIG_MAPSCALE] / 100; SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0))); } else for (i=0; iformat, 0, 0, 0, ALPHA_FUDGE(dark4*(map_image_half_size * use_config[CONFIG_MAPSCALE] / 100 -i) + dark0*i) / (map_image_half_size * use_config[CONFIG_MAPSCALE] / 100))); } if (dark2 == dark0) { dst.x=map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; dst.y=0; dst.w = map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; dst.h = map_image_size * use_config[CONFIG_MAPSCALE] / 100; SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0))); } else for (i=map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; iformat, 0, 0, 0, ALPHA_FUDGE(dark0*(map_image_size* use_config[CONFIG_MAPSCALE] / 100-i) + dark2*(i-map_image_half_size* use_config[CONFIG_MAPSCALE] / 100)) / (map_image_half_size * use_config[CONFIG_MAPSCALE] / 100))); } dst.x= x * map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.y= y * map_image_size * use_config[CONFIG_MAPSCALE] / 100; SDL_BlitSurface(lightmap, NULL, mapsurface, &dst); } else if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST ) { #if 0 int dx,dy; static int *darkx=NULL, *darky=NULL,darkx_allocated=0; /* Generated stored for the darkx[] array. Do it dynamically, but * only allocate if the size needs to be expanded to keep performance * better. darkx could be null in the initial case, but g_realloc should * just treat that as a g_malloc (so according to the man page) */ if (map_image_size * use_config[CONFIG_MAPSCALE] / 100 > darkx_allocated) { darkx = g_realloc(darkx, map_image_size * use_config[CONFIG_MAPSCALE] / 100 * sizeof(int)); darky = g_realloc(darky, map_image_size * use_config[CONFIG_MAPSCALE] / 100 * sizeof(int)); darkx_allocated = map_image_size * use_config[CONFIG_MAPSCALE] / 100; } for( dx= 0; dx < map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; dx++) { darkx[dx]= (dark4*(map_image_half_size * use_config[CONFIG_MAPSCALE] / 100-dx) + dark0*dx) / (map_image_half_size * use_config[CONFIG_MAPSCALE] / 100); } for( dx= map_image_half_size* use_config[CONFIG_MAPSCALE] / 100; dx < map_image_size * use_config[CONFIG_MAPSCALE] / 100; dx++) { darkx[dx] = (dark0*(map_image_size* use_config[CONFIG_MAPSCALE] / 100-dx) + dark2*(dx-map_image_half_size* use_config[CONFIG_MAPSCALE] / 100)) / (map_image_half_size* use_config[CONFIG_MAPSCALE] / 100); } for( dy= 0; dy < map_image_half_size* use_config[CONFIG_MAPSCALE] / 100; dy++) { darky[dy]= (dark1*(map_image_half_size* use_config[CONFIG_MAPSCALE] / 100-dy) + dark0*dy) / (map_image_half_size * use_config[CONFIG_MAPSCALE] / 100); } for( dy= map_image_half_size * use_config[CONFIG_MAPSCALE] / 100; dy < map_image_size * use_config[CONFIG_MAPSCALE] / 100; dy++) { darky[dy] = (dark0*(map_image_size * use_config[CONFIG_MAPSCALE] / 100-dy) + dark3*(dy-map_image_half_size * use_config[CONFIG_MAPSCALE] / 100)) / (map_image_half_size * use_config[CONFIG_MAPSCALE] / 100); } SDL_LockSurface( lightmap); for (dx=0; dxformat, 0, 0, 0,(darkx[dx] + darky[dy])/2)); } #else /*we need additionnal surrounding infos*/ int dark5, dark6, dark7, dark8; if ( (y-1 < 0) || (x+1 >= use_config[CONFIG_MAPWIDTH]) || !mapdata_cell(mx+1, my-1)->darkness) { dark5 = (dark1+dark2)>>1; /*(fast div 2)*/ } else { dark5 = mapdata_cell(mx+1, my-1)->darkness; } if ( (x+1 >= use_config[CONFIG_MAPWIDTH]) || (y+1 >= use_config[CONFIG_MAPHEIGHT]) || !mapdata_cell(mx+1, my+1)->darkness) { dark6 = (dark2+dark3)>>1; } else { dark6 = mapdata_cell(mx+1, my+1)->darkness; } if ( (y+1 >= use_config[CONFIG_MAPHEIGHT]) || (x-1 < 0) || !mapdata_cell(mx-1, my+1)->darkness) { dark7 = (dark3+dark4)>>1; } else { dark7 = mapdata_cell(mx-1, my+1)->darkness; } if ( (x-1 < 0) || (y-1 < 0) || !mapdata_cell(mx-1, my-1)->darkness) { dark8 = (dark4+dark1)>>1; } else { dark8 = mapdata_cell(mx-1, my-1)->darkness; } /*upper left lightmap quarter*/ drawquarterlightmap_sdl(dark8, dark1, dark4, dark0, /*colors*/ map_image_size * use_config[CONFIG_MAPSCALE] / 100, map_image_size * use_config[CONFIG_MAPSCALE] / 100, /*color square size*/ map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, map_image_size * use_config[CONFIG_MAPSCALE] / 100, map_image_size * use_config[CONFIG_MAPSCALE] / 100, /*interpolation region*/ 0, 0); /*where in lightmap to save result*/ /*upper right lightmap quarter*/ drawquarterlightmap_sdl(dark1, dark5, dark0, dark2, /*colors*/ map_image_size * use_config[CONFIG_MAPSCALE] / 100, map_image_size * use_config[CONFIG_MAPSCALE] / 100, /*color square size*/ 0, map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, map_image_size * use_config[CONFIG_MAPSCALE] / 100, /*interpolation region*/ map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, 0); /*where in lightmap to save result*/ /*bottom left lightmap quarter*/ drawquarterlightmap_sdl(dark4, dark0, dark7, dark3, /*colors*/ map_image_size* use_config[CONFIG_MAPSCALE] / 100, map_image_size * use_config[CONFIG_MAPSCALE] / 100, /*color square size*/ map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, 0, map_image_size * use_config[CONFIG_MAPSCALE] / 100, map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, /*interpolation region*/ 0, map_image_half_size * use_config[CONFIG_MAPSCALE] / 100); /*where in lightmap to save result*/ /*bottom right lightmap quarter*/ drawquarterlightmap_sdl(dark0, dark2, dark3, dark6, /*colors*/ map_image_size* use_config[CONFIG_MAPSCALE] / 100, map_image_size * use_config[CONFIG_MAPSCALE] / 100, /*color square size*/ 0, 0, map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, /*interpolation region*/ map_image_half_size * use_config[CONFIG_MAPSCALE] / 100, map_image_half_size * use_config[CONFIG_MAPSCALE] / 100); /*where in lightmap to save result*/ #endif dst.w= map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.h= map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.x= x * map_image_size * use_config[CONFIG_MAPSCALE] / 100; dst.y= y * map_image_size * use_config[CONFIG_MAPSCALE] / 100; SDL_UnlockSurface(lightmap); SDL_BlitSurface(lightmap, NULL, mapsurface, &dst); } } /** * Draw anything in adjacent squares that could smooth on given square * mx,my square to smooth on. you should not call this function to * smooth on a 'completly black' square. (simply for visual result) * layer layer to examine (we smooth only one layer at a time) * dst place on the mapwindow to draw * * @param mx * @param my * @param layer * @param dst */ static void drawsmooth_sdl (int mx,int my,int layer,SDL_Rect dst) { static int dx[8]= {0,1,1,1,0,-1,-1,-1}; static int dy[8]= {-1,-1,0,1,1,1,0,-1}; static int bweights[8]= {2,0,4,0,8,0,1,0}; static int cweights[8]= {0,2,0,4,0,8,0,1}; static int bc_exclude[8]= { 1+2,/*north exclude northwest (bit0) and northeast(bit1)*/ 0, 2+4,/*east exclude northeast and southeast*/ 0, 4+8,/*and so on*/ 0, 8+1, 0 }; int partdone[8]= {0,0,0,0,0,0,0,0}; int slevels[8]; int sfaces[8]; int i,weight,weightC; int emx,emy; int smoothface; int hasFace = 0; SDL_Rect src; for (i=0; i<=layer; i++) { hasFace |= mapdata_cell(mx, my)->heads[i].face; } if (!hasFace || !mapdata_can_smooth(mx, my, layer)) { return; } src.w=dst.w; src.h=dst.h; for (i=0; i<8; i++) { emx=mx+dx[i]; emy=my+dy[i]; if (!mapdata_contains(emx, emy)) { slevels[i]=0; sfaces[i]=0; /*black picture*/ } else if (mapdata_cell(emx, emy)->smooth[layer]<=mapdata_cell(mx, my)->smooth[layer]) { slevels[i]=0; sfaces[i]=0; /*black picture*/ } else { slevels[i]=mapdata_cell(emx, emy)->smooth[layer]; sfaces[i]=pixmaps[mapdata_cell(emx, emy)->heads[layer].face]->smooth_face; } } /* ok, now we have a list of smoothlevel higher than current square. * there are at most 8 different levels. so... let's check 8 times * for the lowest one (we draw from botto to top!). */ while (1) { int lowest = -1; for (i=0; i<8; i++) { if ( (slevels[i]>0) && (!partdone[i]) && ((lowest<0) || (slevels[i]map_image) || (pixmaps[smoothface] == pixmaps[0])) { continue; /*don't have the picture associated*/ } if (weight>0) { src.x=map_image_size*weight * use_config[CONFIG_MAPSCALE] / 100; src.y=0; if (mapdata_cell(mx, my)->cleared) { if (SDL_BlitSurface(pixmaps[smoothface]->fog_image, &src, mapsurface, &dst)) { do_SDL_error( "BlitSurface", __FILE__, __LINE__); } } else { if (SDL_BlitSurface(pixmaps[smoothface]->map_image, &src, mapsurface, &dst)) { do_SDL_error( "BlitSurface", __FILE__, __LINE__); } } } if (weightC>0) { src.x=map_image_size*weightC* use_config[CONFIG_MAPSCALE] / 100; src.y=map_image_size* use_config[CONFIG_MAPSCALE] / 100; if (mapdata_cell(mx, my)->cleared) { if (SDL_BlitSurface(pixmaps[smoothface]->fog_image, &src, mapsurface, &dst)) { do_SDL_error( "BlitSurface", __FILE__, __LINE__); } } else { if (SDL_BlitSurface(pixmaps[smoothface]->map_image, &src, mapsurface, &dst)) { do_SDL_error( "BlitSurface", __FILE__, __LINE__); } } } }/*while there's some smooth to do*/ } /** * Replacment of sdl_square_need_redraw logic. use of sdl_square_need_redraw * is relatively inefficient becuase it is called for every space (hence * function call overhead), but also has 4 checks to make sure the neighbor * space is within valid range, and if non tile mode, performs that check at * least 4 times per space. * This is much more efficient, because our redrawbitmap array is large enough * we don't need those checks - we know we are always safe to go one outside * the bounds (hence, the +1 in the coordinate values) */ static void update_redrawbitmap(void) { int mx,my, x,y; memset(redrawbitmap, 0, (use_config[CONFIG_MAPWIDTH]+2) * (use_config[CONFIG_MAPHEIGHT]+2)); for( x= 0; xneed_update) { redrawbitmap[x + 1 + (y+1) * use_config[CONFIG_MAPWIDTH]] = 1; /* If this space has changed, and using non tile lighting, * we need to update the neighbor spaces. Ideally, we'd * have a flag just to denote lighting changes, since * that is handled on a different surface anyways. */ if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL || use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST) { /* This is where having redrawbitmap bigger pays off - don't have * to check to see if values are within redrawbitmap is within bounds */ redrawbitmap[x + (y+1) * use_config[CONFIG_MAPWIDTH]] = 1; redrawbitmap[x + 2 + (y+1) * use_config[CONFIG_MAPWIDTH]] = 1; redrawbitmap[x + 1 + (y) * use_config[CONFIG_MAPWIDTH]] = 1; redrawbitmap[x + 1 + (y+2) * use_config[CONFIG_MAPWIDTH]] = 1; } /* In best mode, have to update diaganols in addition*/ if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST) { redrawbitmap[x + (y) * use_config[CONFIG_MAPWIDTH]] = 1; redrawbitmap[x + 2 + (y) * use_config[CONFIG_MAPWIDTH]] = 1; redrawbitmap[x + (y+2) * use_config[CONFIG_MAPWIDTH]] = 1; redrawbitmap[x + 2 + (y+2) * use_config[CONFIG_MAPWIDTH]] = 1; } } else if (mapdata_cell(mx, my)->need_resmooth) { redrawbitmap[x + 1 + (y+1) * use_config[CONFIG_MAPWIDTH]] = 1; } } } } /** * * @param ax * @param ay * @param mx * @param my */ static void display_mapcell(int ax, int ay, int mx, int my) { SDL_Rect dst, src; int layer, scaled_image_size = map_image_size * use_config[CONFIG_MAPSCALE] / 100; /* First, we need to black out this space. */ dst.x = ax*scaled_image_size; dst.y = ay*scaled_image_size; dst.w = scaled_image_size; dst.h = scaled_image_size; SDL_FillRect(mapsurface, &dst, SDL_MapRGB(mapsurface->format, 0, 0, 0)); /* now draw the different layers. Only draw if using fog of war or the * space isn't clear. */ if (use_config[CONFIG_FOGWAR] || !mapdata_cell(mx, my)->cleared) { for (layer=0; layer 0 && pixmaps[face]->map_image != NULL) { int w = pixmaps[face]->map_width; int h = pixmaps[face]->map_height; /* add one to the size values to take into account the actual width of the space */ src.x = w-scaled_image_size; src.y = h-scaled_image_size; src.w = scaled_image_size; src.h = scaled_image_size; dst.x = ax*scaled_image_size; dst.y = ay*scaled_image_size; if (mapdata_cell(mx, my)->cleared) { if (SDL_BlitSurface(pixmaps[face]->fog_image, &src, mapsurface, &dst)) { do_SDL_error( "BlitSurface", __FILE__, __LINE__); } } else { if (SDL_BlitSurface(pixmaps[face]->map_image, &src, mapsurface, &dst)) { do_SDL_error( "BlitSurface", __FILE__, __LINE__); } } } /* Sometimes, it may happens we need to draw the smooth while there * is nothing to draw at that layer (but there was something at lower * layers). This is handled here. The else part is to take into account * cases where the smooth as already been handled 2 code lines before */ if (use_config[CONFIG_SMOOTH]) { drawsmooth_sdl (mx,my,layer,dst); } /* draw big faces last (should overlap other objects) */ face = mapdata_bigface(ax, ay, layer, &sx, &sy); if (face > 0 && pixmaps[face]->map_image != NULL) { /* We have to handle images that are not an equal * multiplier of map_image_size. See * display_mapcell() in gtk-v2/src/map.c for * more details on this logic, since it is basically * the same. */ int dx, dy, sourcex, sourcey, offx, offy; dx = pixmaps[face]->map_width % scaled_image_size; offx = dx?(scaled_image_size-dx):0; if (sx) { sourcex = sx * scaled_image_size - offx ; offx=0; } else { sourcex=0; } dy = (pixmaps[face]->map_height) % scaled_image_size; offy = dy?(scaled_image_size-dy):0; if (sy) { sourcey = sy * scaled_image_size - offy; offy=0; } else { sourcey=0; } src.x = sourcex; src.y = sourcey; src.w = scaled_image_size - offx; src.h = scaled_image_size - offy; dst.x = ax*scaled_image_size + offx; dst.y = ay*scaled_image_size + offy; if (mapdata_cell(mx, my)->cleared) { if (SDL_BlitSurface(pixmaps[face]->fog_image, &src, mapsurface, &dst)) { do_SDL_error( "BlitSurface", __FILE__, __LINE__); } } else { if (SDL_BlitSurface(pixmaps[face]->map_image, &src, mapsurface, &dst)) { do_SDL_error( "BlitSurface", __FILE__, __LINE__); } } } /* else for processing the layers */ } } if (use_config[CONFIG_LIGHTING] == CFG_LT_TILE) { dst.x = ax*scaled_image_size; dst.y = ay*scaled_image_size; dst.w = scaled_image_size; dst.h = scaled_image_size; /* Note - Instead of using a lightmap, I just fillrect * directly onto the map surface - I would think this should be * faster */ if (mapdata_cell(mx, my)->darkness == 255) { SDL_FillRect(mapsurface,&dst, SDL_MapRGB(mapsurface->format, 0, 0, 0)); } else if (mapdata_cell(mx, my)->darkness != 0) { SDL_SetAlpha(lightmap, SDL_SRCALPHA|SDL_RLEACCEL, mapdata_cell(mx, my)->darkness); SDL_BlitSurface(lightmap, NULL, mapsurface, &dst); } } else if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL || use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST) { do_sdl_per_pixel_lighting(ax, ay, mx, my); } } /** * Generates a map in SDL mode. * * I had to totally change the logic on how we do this in SDL mode - to support * variable sized images, the old method of generating each space does not * work, as one space may spill over to the other. Instead, we first blit the * bottom layer, then the layer above that, and so on. This results in the map * being drawn a bit more correctly. In fact, that logic actually isn't * needed, as with the new map commands, we know the offset and size of the * images. * * The logic here only redraws spaces that change. The logic in the * common/commands.c files the odd layers with links for 'big images'. For * objects on these layers, we look at the size_x and size_y values to * determine the offset from which we should be blitting. * * Old notes, but left in: * The performance here is very good in most cases - about 30 ms (on my system) * is used just for my flip at the bottom of the function, drawing only what is * needed generally saves a lot of time (<15 ms in most cases) compared to the * 80-120 ms usually needed on a 15x15 map. * * @param redraw */ void sdl_gen_map(int redraw) { int x, y, num_spaces = 0, num_drawn = 0; if (redrawbitmap == NULL) { return; } update_redrawbitmap(); for( x= 0; xneed_update = 0; mapdata_cell(pl_pos.x+x, pl_pos.y+y)->need_resmooth = 0; } } } SDL_Flip(mapsurface); } /** * * @param dx * @param dy */ int sdl_mapscroll(int dx, int dy) { /* Don't sdl_gen_map should take care of the redraw */ // If we are more than about 15 tiles of movement, then // Don't scroll. This should avoid a segfault. if (dx > 15 || dx < -15 || dy > 15 || dy < 15) return 0; /* a copy of what pngximage does except sdl specfic * mapsurface->pitch is the length of a scanline in bytes * including alignment padding */ SDL_LockSurface( mapsurface); if( dy < 0) { int offset= mapsurface->pitch * (-dy*map_image_size* use_config[CONFIG_MAPSCALE] / 100); memmove( mapsurface->pixels + offset, mapsurface->pixels, mapsurface->pitch * (mapsurface->h + dy*map_image_size* use_config[CONFIG_MAPSCALE] / 100) ); } else if( dy > 0) { int offset= mapsurface->pitch * (dy*map_image_size * use_config[CONFIG_MAPSCALE] / 100); memmove( mapsurface->pixels, mapsurface->pixels + offset, mapsurface->pitch * (mapsurface->h - dy*map_image_size* use_config[CONFIG_MAPSCALE] / 100) ); } if (dx) { int y; for( y= 0; y < mapsurface->h; y++) { if( dx < 0) { char* start_of_row= mapsurface->pixels + mapsurface->pitch * y; int offset= ( mapsurface->format->BytesPerPixel * map_image_size* use_config[CONFIG_MAPSCALE] / 100 * -dx); memmove( start_of_row + offset, start_of_row, mapsurface->pitch - offset); } else { char* start_of_row= mapsurface->pixels + mapsurface->pitch * y; int offset= ( mapsurface->format->BytesPerPixel * map_image_size* use_config[CONFIG_MAPSCALE] / 100 * dx); memmove( start_of_row, start_of_row + offset, mapsurface->pitch - offset); } } } SDL_UnlockSurface( mapsurface); return 1; } #endif