Crossfire Client, Branches  R11627
sdl.c
Go to the documentation of this file.
1 const char * const rcsid_gtk_sdl_c =
2  "$Id: sdl.c 9190 2008-06-01 08:53:05Z anmaster $";
3 
4 /*
5  CrossFire, A Multiplayer game for X-windows
6 
7  Copyright (C) 2001 Mark Wedel & Crossfire Development Team
8  Copyright (C) 1992 Frank Tore Johansen
9 
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with this program; if not, write to the Free Software
22  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 
24  The author can be reached via e-mail to crossfire-devel@real-time.com
25 */
26 
27 #include <config.h>
28 #ifdef HAVE_SDL
29 #include <client-types.h>
30 #include <SDL.h>
31 #include <SDL_image.h>
32 
33 /* Pick up the gtk headers we need */
34 #include <gtk/gtk.h>
35 #include <gdk/gdkx.h>
36 #include <gdk/gdkkeysyms.h>
37 
38 #include "gx11.h"
39 #include <client.h>
40 #include "mapdata.h"
41 
42 /* Actual SDL surface the game view is painted on */
43 SDL_Surface* mapsurface;
44 static SDL_Surface* lightmap;
45 static SDL_Surface* fogmap;
46 
47 
48 /* Move some of the SDL code to this file here. This makes it easier to share
49  * between the gnome and gtk client. It also reduces the length of both the gx11.c
50  * and gnome.c file. It also is more readable, as not as many #ifdef SDL.. #endif
51  * constructs are needed.
52  * Note that there may still be some SDL code in gx11.c - some areas are embedded
53  * so much that it is not easy to remove.
54  */
55 
56 /* these should generally be included by the file including this file. */
57 #include <SDL.h>
58 #include <SDL_image.h>
59 
60 
61 static void do_SDL_error(const char *SDL_function, const char *file, int line)
62 {
63  LOG(LOG_CRITICAL,SDL_function,"SDL error in file %s line %d\n%s",
64  file, line, SDL_GetError());
65  SDL_Quit();
66  exit( 1);
67 }
68 
69 /*
70  * Set the pixel at (x, y) to the given value
71  * NOTE: The surface must be locked before calling this!
72  * This function is directly grabbed from the SDL docs.
73  * Note this is not currently used, but is useful enough
74  * that it should be included.
75  */
76 static void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
77 {
78  int bpp = surface->format->BytesPerPixel;
79  /* Here p is the address to the pixel we want to set */
80  Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
81 
82  switch(bpp) {
83  case 1:
84  *p = pixel;
85  break;
86 
87  case 2:
88  *(Uint16 *)p = pixel;
89  break;
90 
91  case 3:
92  if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
93  p[0] = (pixel >> 16) & 0xff;
94  p[1] = (pixel >> 8) & 0xff;
95  p[2] = pixel & 0xff;
96  } else {
97  p[0] = pixel & 0xff;
98  p[1] = (pixel >> 8) & 0xff;
99  p[2] = (pixel >> 16) & 0xff;
100  }
101  break;
102 
103  case 4:
104  *(Uint32 *)p = pixel;
105  break;
106  }
107 }
108 
109 static void overlay_grid( int re_init, int ax, int ay)
110 {
111 
112  static SDL_Surface* grid_overlay;
113 
114  static int first_pass;
115 
116  int x= 0;
117  int y= 0;
118  SDL_Rect dst;
119  Uint32 *pixel;
120  SDL_PixelFormat* fmt;
121 
122  /* Need to convert back to screen coordinates */
123  ax-= pl_pos.x;
124  ay-= pl_pos.y;
125 
126  if( re_init == TRUE)
127  {
128  if( grid_overlay)
129  SDL_FreeSurface( grid_overlay);
130 
131  first_pass= 0;
132  grid_overlay= NULL;
133  }
134 
135  if( grid_overlay == NULL)
136  {
137  grid_overlay= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA,
139  use_config[CONFIG_MAPHEIGHT]*map_image_size,
140  mapsurface->format->BitsPerPixel,
141  mapsurface->format->Rmask,
142  mapsurface->format->Gmask,
143  mapsurface->format->Bmask,
144  mapsurface->format->Amask);
145  if( grid_overlay == NULL)
146  do_SDL_error( "CreateRGBSurface", __FILE__, __LINE__);
147 
148  grid_overlay= SDL_DisplayFormatAlpha( grid_overlay);
149 
150  first_pass= 0;
151  }
152 
153  /*
154  * If this is our first time drawing the grid, we need to build up the
155  * grid overlay
156  */
157  if( first_pass== 0)
158  {
159 
160  /* Red pixels around the edge and along image borders
161  * fully transparent pixels everywhere else
162  */
163 
164  fmt= grid_overlay->format;
165  for( x= 0; x < map_image_size*use_config[CONFIG_MAPWIDTH]; x++)
166  {
167  for( y= 0; y < map_image_size*use_config[CONFIG_MAPHEIGHT]; y++)
168  {
169  /* FIXME: Only works for 32 bit displays right now */
170  pixel= (Uint32*)grid_overlay->pixels+y*grid_overlay->pitch/4+x;
171 
172  if( x == 0 || y == 0 ||
173  ((x % map_image_size) == 0) || ((y % map_image_size) == 0 ) ||
174  y == use_config[CONFIG_MAPHEIGHT]*map_image_size-1 || x == use_config[CONFIG_MAPWIDTH]*map_image_size -1 )
175  {
176  *pixel= SDL_MapRGBA( fmt, 255, 0, 0, SDL_ALPHA_OPAQUE);
177  }
178  else
179  {
180  *pixel= SDL_MapRGBA( fmt, 0, 0, 0, SDL_ALPHA_TRANSPARENT);
181  }
182  }
183  }
184  first_pass= 1;
185 
186  /*
187  * If this is our first pass then we need to overlay the entire grid
188  * now. Otherwise we just update the tile we are on
189  */
190  dst.x= 0;
191  dst.y= 0;
192  dst.w= map_image_size*use_config[CONFIG_MAPWIDTH];
193  dst.h= map_image_size*use_config[CONFIG_MAPHEIGHT];
194  SDL_BlitSurface( grid_overlay, NULL, mapsurface, &dst);
195  }
196  else
197  {
198  dst.x= ax* map_image_size;
199  dst.y= ay* map_image_size;
200  dst.w= map_image_size;
201  dst.h= map_image_size;
202  /* One to one pixel mapping of grid and mapsurface so we
203  * can share the SDL_Rect
204  */
205  SDL_BlitSurface( grid_overlay, &dst, mapsurface, &dst);
206  }
207 
208  return;
209 }
210 
211 /*
212  * Takes two args, the first is the GtkWindow to draw on, this should always
213  * be 'drawingarea'. The second is a boolean, if 0 then the whole
214  * SDL system in initialized or reinited if already run once before,
215  * if non zero then only the lightmap is rebuilt, if we switch between
216  * per-pixel or per-tile lighting
217  */
218 void init_SDL( GtkWidget* sdl_window, int just_lightmap)
219 {
220 
221  char SDL_windowhack[32];
222 
223  if( just_lightmap == 0) {
224  g_assert( sdl_window != NULL);
225  if( SDL_WasInit( SDL_INIT_VIDEO) != 0) {
226  if( lightmap)
227  SDL_FreeSurface( lightmap);
228  if( mapsurface)
229  SDL_FreeSurface( mapsurface);
230  SDL_Quit();
231  }
232 
233  /*
234  * SDL hack to tell SDL which xwindow to paint onto
235  */
236  sprintf( SDL_windowhack, "SDL_WINDOWID=%ld",
237  GDK_WINDOW_XWINDOW(sdl_window->window) );
238  putenv( SDL_windowhack);
239 
240  if( SDL_Init( SDL_INIT_VIDEO) < 0)
241  {
242  LOG(LOG_CRITICAL,"gtk::init_SDL", "Could not initialize SDL: %s", SDL_GetError());
243  gtk_main_quit();
244  }
245 
247  SDL_HWSURFACE|SDL_DOUBLEBUF);
248 
249  if( mapsurface == NULL)
250  {
251  do_SDL_error( "SetVideoMode", __FILE__, __LINE__);
252  }
253 
254  if( fogmap)
255  SDL_FreeSurface( fogmap);
256 
257  fogmap= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA, map_image_size,
259  mapsurface->format->BitsPerPixel,
260  mapsurface->format->Rmask,
261  mapsurface->format->Gmask,
262  mapsurface->format->Bmask,
263  mapsurface->format->Amask);
264 
265  if( fogmap == NULL)
266  {
267  do_SDL_error( "SDL_CreateRGBSurface", __FILE__, __LINE__);
268  }
269 
270  /*
271  * This is a persurface alpha value, not an alpha channel value.
272  * So this surface doesn't actually need a full alpha channel
273  */
274  if( SDL_SetAlpha( fogmap, SDL_SRCALPHA|SDL_RLEACCEL, 128) < 0)
275  {
276  do_SDL_error( "SDL_SetAlpha", __FILE__, __LINE__);
277  }
278  }
279 
280  if( just_lightmap != 0 && lightmap)
281  SDL_FreeSurface( lightmap);
282 
283  lightmap= SDL_CreateRGBSurface( SDL_HWSURFACE|SDL_SRCALPHA, map_image_size,
285  mapsurface->format->BitsPerPixel,
286  mapsurface->format->Rmask,
287  mapsurface->format->Gmask,
288  mapsurface->format->Bmask,
289  mapsurface->format->Amask);
290  if( lightmap == NULL)
291  {
292  do_SDL_error( "SDL_CreateRGBSurface", __FILE__, __LINE__);
293  }
294 
296  {
297  /* Convert surface to have a full alpha channel if we are doing
298  * per-pixel lighting */
299  lightmap= SDL_DisplayFormatAlpha( lightmap);
300  if( lightmap == NULL)
301  {
302  do_SDL_error( "DisplayFormatAlpha", __FILE__, __LINE__);
303  }
304  }
305 
307  {
308  overlay_grid( TRUE, 0, 0);
309  }
310 }
311 
312 
313 /* Draw a alpha square on lightmap. Topleft corner is at startx,starty.
314  * values for topleft, topright, bottomleft,bottomright corners are knowns
315  * This use bilinear interpolation for other points. Width and heights are given
316  * for surrouding known values square. Interpolation is done in a small square whose
317  * coordinates are given by start{x|y} and end{x|y}
318  * dest{x|y} is top-left corner in destination map.
319  * Tchize 22 May 2004
320  */
321 
322 static void drawquarterlightmap_sdl(int tl, int tr, int bl, int br, /*colors*/
323  int width, int height, /*color square size*/
324  int startx, int starty, int endx, int endy, /*interpolation region*/
325  int destx, int desty){ /*where in lightmap to save result*/
326  int x,y;
327  int top,bottom,val;
328  for (x=startx;x<endx;x++){
329  top= ((x*(tr-tl))/ width)+tl; /*linear interpolation for top color*/
330  bottom= ((x*(br-bl))/ width)+bl; /*linear interpolation for bottom color*/
331  for (y=starty;y<endy;y++){
332  val=((y*(bottom-top))/height)+top; /*linear interpolation between top and bottom*/
333  if (val>255)
334  val=255;
335  if (val<0)
336  val=0;
337  /*printf("writing pel at %d,%d\n",destx+x,desty+y);*/
338  putpixel(lightmap, destx+x-startx, desty+y-starty,
339  SDL_MapRGBA(lightmap->format, 0, 0, 0, val));
340  }
341  }
342 }
343 
344 /* Do the lighting on a per pixel basis.
345  * x and y are coordinates on the drawable map surfaces (but in terms of
346  * spaces, not pixels). mx and my are indexes into the
347  * the_map.cells[][] array.
348  * All the below goes out and figures lighting for each pixel on
349  * the space, and creates a surface (with alpha) that is then put on
350  * top of the exiting map space.
351  *
352  * TODO: I think it is possible to greatly speed this up by using
353  * pre-generated darkness masks. Doing all the possibilities
354  * would be 3125 images (5 positions, each with 5 values, 5^5),
355  * Doing it based on quadrants would only reduce that to 1024.
356  * But I _think_ it may be possible to do this with just 64 images
357  * (2^5 + one 90 degree rotation of the same) based on quadrants.
358  * ie, do a 16x16 image with the 5 gradiants (0,64,128,255 at the
359  * left, and each of those values at the right). Then do the same
360  * idea for top and bottom. For any single quadrant, you would
361  * then merge thse two values (which can be done with a fast blit),
362  * corresponding to the right values, and you do the same thing for
363  * the other four quadrants. Note this only works so long as
364  * 4 lighting values are used - if more are added, this quickly
365  * breaks. Also, if lighting colored effects are desired,
366  * this also doesn't work very well.
367  *
368  * For now, I've just kept the old logic. MSW 2001-10-09
369  */
370 
371 /* See note below about ALPHA_FUDGE - used to adjust lighting effects some */
372 
373 #define ALPHA_FUDGE(x) (2*(x) / 3)
374 static void do_sdl_per_pixel_lighting(int x, int y, int mx, int my)
375 {
376  int dark0, dark1, dark2, dark3, dark4;
377  SDL_Rect dst;
378 
379  /* I use dark0 -> dark4 in the order to keep it similar to
380  * the old code.
381  */
382  dark0 = the_map.cells[mx][my].darkness;
383 
384  if (y-1 < 0 || !the_map.cells[mx][my-1].have_darkness) dark1 = dark0;
385  else dark1 = the_map.cells[mx][my-1].darkness;
386 
387  if (x+1 >= use_config[CONFIG_MAPWIDTH] || !the_map.cells[mx+1][my].have_darkness) dark2 = dark0;
388  else dark2 = the_map.cells[mx+1][my].darkness;
389 
390  if (y+1 >= use_config[CONFIG_MAPHEIGHT] || !the_map.cells[mx][my+1].have_darkness) dark3 = dark0;
391  else dark3 = the_map.cells[mx][my+1].darkness;
392 
393  if (x-1 < 0 || !the_map.cells[mx-1][my].have_darkness) dark4 = dark0;
394  else dark4 = the_map.cells[mx-1][my].darkness;
395 
396  /* If they are all the same, processing is easy
397  *
398  * Note, the best lightining algorithm also uses diagonals
399  * so we should check the diagonals are same too
400  * We don't check for now, simply do all raw computation on best mode
401  * Tchize 19 may 2004
402  */
403  if (dark0 == dark1 && dark0 == dark2 && dark0 == dark3 && dark0 == dark4 && (use_config[CONFIG_LIGHTING] != CFG_LT_PIXEL_BEST)) {
404  dst.x = x * map_image_size;
405  dst.y = y * map_image_size;
406  dst.w = map_image_size;
407  dst.h = map_image_size;
408 
409  if (dark0 == 255) {
410  SDL_FillRect(mapsurface,&dst, SDL_MapRGB(mapsurface->format, 0, 0, 0));
411  } else if (the_map.cells[mx][my].darkness != 0) {
412  SDL_FillRect(lightmap,NULL, SDL_MapRGBA(lightmap->format, 0, 0, 0, the_map.cells[mx][my].darkness));
413  SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
414  }
415  return;
416  }
417 
418 
420  /* This almost works as well as the per pixel code below, but does have some various
421  * artifacts in the drawing. It uses the same logic as the per pixel code below,
422  * bit since SDL does the blit, the alpha handling ends up being different
423  * (I think it ends up being additive). This results in the darkness being
424  * darker, but you also don't get the smooth effects. If you divide all the values
425  * by 2 (change ALPHA_FUDGE), the blending is smooth, but now the things are not dark
426  * enough, so the blending aganst solid black spaces does not look good.
427  * The reason this code is of interest is that on my system, it is about 50%
428  * faster than the code below (25 ms to darkness the church in the starting
429  * town vs 50 ms for the code further down)
430  * Setting ALPHA_FUDGE to 2/3 seems to reduce the artifacts described above
431  * to fairly minimal levels, while still keeping things dark enough.
432  * MSW 2001-10-12
433  */
434 
435  int i;
436 
437  if (dark1 == dark0) {
438  /* If we don't have usable darkness at the top, then this entire region
439  * should be the same value. Likewise, if the top value and center value
440  * are the same, we can do the entire region.
441  */
442  dst.x=0;
443  dst.y=0;
444  dst.w = map_image_size;
445  dst.h = map_image_half_size;
446  SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0)));
447  }
448  else for (i=0; i<map_image_half_size; i++) {
449  /* Need to do it line by line */
450 
451  dst.x = 0;
452  dst.y = i;
453  dst.w = map_image_size;
454  dst.h = 1;
455  SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0,
456  ALPHA_FUDGE((map_image_half_size - i) * dark1 + i * dark0)/map_image_half_size));
457 
458  }
459  /* All the following blocks are basically the same as above, just different
460  * darkness areas.
461  */
462  if (dark3 == dark0) {
463  dst.x=0;
464  dst.y=map_image_half_size;
465  dst.w = map_image_size;
466  dst.h = map_image_half_size;
467  SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0)));
468  }
469  else for (i=map_image_half_size; i<map_image_size; i++) {
470  /* Need to do it line by line */
471 
472  dst.x = 0;
473  dst.y = i;
474  dst.w = map_image_size;
475  dst.h = 1;
476  SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0,
477  ALPHA_FUDGE(dark0*(map_image_size-i) + dark3*(i-map_image_half_size)) / map_image_half_size));
478  }
479  /* Blit this to the screen now. Otherwise, we need to look at the alpha values
480  * and re-average.
481  */
482 
483  dst.x= x * map_image_size;
484  dst.y= y * map_image_size;
485  SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
486 
487  if (dark4 == dark0) {
488  dst.x=0;
489  dst.y=0;
490  dst.w = map_image_half_size;
491  dst.h = map_image_size;
492  SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0)));
493  }
494  else for (i=0; i<map_image_half_size; i++) {
495  /* Need to do it line by line */
496  dst.x = i;
497  dst.y = 0;
498  dst.w = 1;
499  dst.h = map_image_size;
500  SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0,
501  ALPHA_FUDGE(dark4*(map_image_half_size-i) + dark0*i) / map_image_half_size));
502  }
503  if (dark2 == dark0) {
504  dst.x=map_image_half_size;
505  dst.y=0;
506  dst.w = map_image_half_size;
507  dst.h = map_image_size;
508  SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0, ALPHA_FUDGE(dark0)));
509  }
510  else for (i=map_image_half_size; i<map_image_size; i++) {
511  /* Need to do it line by line */
512 
513  dst.x = i;
514  dst.y = 0;
515  dst.w = 1;
516  dst.h = map_image_size;
517  SDL_FillRect(lightmap, &dst, SDL_MapRGBA(lightmap->format, 0, 0, 0,
518  ALPHA_FUDGE(dark0*(map_image_size-i) + dark2*(i-map_image_half_size)) / map_image_half_size));
519  }
520  dst.x= x * map_image_size;
521  dst.y= y * map_image_size;
522  SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
523  } else if (use_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST ) {
524 #if 0
525  int dx,dy;
526  static int *darkx=NULL, *darky=NULL,darkx_allocated=0;
527 
528  /* Generated stored for the darkx[] array. Do it dynamically, but
529  * only allocate if the size needs to be expanded to keep performance
530  * better. darkx could be null in the initial case, but realloc should
531  * just treat that as a malloc (so according to the man page)
532  */
533  if (map_image_size > darkx_allocated) {
534  darkx = realloc(darkx, map_image_size * sizeof(int));
535  darky = realloc(darky, map_image_size * sizeof(int));
536  darkx_allocated = map_image_size;
537  }
538 
539  for( dx= 0; dx < map_image_half_size; dx++)
540  darkx[dx]= (dark4*(map_image_half_size-dx) + dark0*dx) / map_image_half_size;
541  for( dx= map_image_half_size; dx < map_image_size; dx++)
542  darkx[dx] = (dark0*(map_image_size-dx) + dark2*(dx-map_image_half_size)) / map_image_half_size;
543 
544  for( dy= 0; dy < map_image_half_size; dy++)
545  darky[dy]= (dark1*(map_image_half_size-dy) + dark0*dy) / map_image_half_size;
546  for( dy= map_image_half_size; dy < map_image_size; dy++)
547  darky[dy] = (dark0*(map_image_size-dy) + dark3*(dy-map_image_half_size)) / map_image_half_size;
548 
549  SDL_LockSurface( lightmap);
550 
551  for (dx=0; dx<map_image_size; dx++)
552  for (dy=0; dy<map_image_size; dy++)
553  putpixel(lightmap, dx, dy, SDL_MapRGBA(lightmap->format, 0, 0, 0,(darkx[dx] + darky[dy])/2));
554 #else
555  /*we need additionnal surrounding infos*/
556  int dark5, dark6, dark7, dark8;
557  if ( (y-1 < 0) || (x+1 >= use_config[CONFIG_MAPWIDTH])
558  || !the_map.cells[mx+1][my-1].have_darkness) dark5 = (dark1+dark2)>>1; /*(fast div 2)*/
559  else dark5 = the_map.cells[mx+1][my-1].darkness;
560 
561  if ( (x+1 >= use_config[CONFIG_MAPWIDTH])
562  || (y+1 >= use_config[CONFIG_MAPHEIGHT])
563  || !the_map.cells[mx+1][my+1].have_darkness) dark6 = (dark2+dark3)>>1;
564  else dark6 = the_map.cells[mx+1][my+1].darkness;
565 
566  if ( (y+1 >= use_config[CONFIG_MAPHEIGHT]) || (x-1 < 0)
567  || !the_map.cells[mx-1][my+1].have_darkness) dark7 = (dark3+dark4)>>1;
568  else dark7 = the_map.cells[mx-1][my+1].darkness;
569 
570  if ( (x-1 < 0) || (y-1 < 0)
571  || !the_map.cells[mx-1][my-1].have_darkness) dark8 = (dark4+dark1)>>1;
572  else dark8 = the_map.cells[mx-1][my-1].darkness;
573  /*upper left lightmap quarter*/
574  drawquarterlightmap_sdl(dark8, dark1, dark4, dark0, /*colors*/
575  map_image_size, map_image_size, /*color square size*/
576  map_image_half_size, map_image_half_size, map_image_size, map_image_size, /*interpolation region*/
577  0, 0); /*where in lightmap to save result*/
578  /*upper right lightmap quarter*/
579  drawquarterlightmap_sdl(dark1, dark5, dark0, dark2, /*colors*/
580  map_image_size, map_image_size, /*color square size*/
581  0, map_image_half_size, map_image_half_size, map_image_size, /*interpolation region*/
582  map_image_half_size, 0); /*where in lightmap to save result*/
583  /*bottom left lightmap quarter*/
584  drawquarterlightmap_sdl(dark4, dark0, dark7, dark3, /*colors*/
585  map_image_size, map_image_size, /*color square size*/
586  map_image_half_size, 0, map_image_size, map_image_half_size, /*interpolation region*/
587  0, map_image_half_size); /*where in lightmap to save result*/
588  /*bottom right lightmap quarter*/
589  drawquarterlightmap_sdl(dark0, dark2, dark3, dark6, /*colors*/
590  map_image_size, map_image_size, /*color square size*/
591  0, 0, map_image_half_size, map_image_half_size, /*interpolation region*/
592  map_image_half_size, map_image_half_size); /*where in lightmap to save result*/
593 #endif
594  dst.w= map_image_size;
595  dst.h= map_image_size;
596  dst.x= x * map_image_size;
597  dst.y= y * map_image_size;
598  SDL_UnlockSurface(lightmap);
599  SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
600  }
601 }
602 /* Draw anything in adjacent squares that could smooth on given square
603  * mx,my square to smooth on. you should not call this function to
604  * smooth on a 'completly black' square. (simply for visual result)
605  * layer layer to examine (we smooth only one layer at a time)
606  * dst place on the mapwindow to draw
607  */
608 static void drawsmooth_sdl (int mx,int my,int layer,SDL_Rect dst){
609  static int dx[8]={0,1,1,1,0,-1,-1,-1};
610  static int dy[8]={-1,-1,0,1,1,1,0,-1};
611  static int bweights[8]={2,0,4,0,8,0,1,0};
612  static int cweights[8]={0,2,0,4,0,8,0,1};
613  static int bc_exclude[8]={
614  1+2,/*north exclude northwest (bit0) and northeast(bit1)*/
615  0,
616  2+4,/*east exclude northeast and southeast*/
617  0,
618  4+8,/*and so on*/
619  0,
620  8+1,
621  0
622  };
623  int partdone[8]={0,0,0,0,0,0,0,0};
624  int slevels[8];
625  int sfaces[8];
626  int i,lowest,weight,weightC;
627  int emx,emy;
628  int smoothface;
629  int hasFace = 0;
630  SDL_Rect src;
631  for (i=0;i<=layer;i++)
632  hasFace |= the_map.cells[mx][my].heads[i].face;
633  if (!hasFace
634  || !CAN_SMOOTH(the_map.cells[mx][my], layer)) {
635  return;
636  }
637 
638  src.w = dst.w;
639  src.h = dst.h;
640 
641  for (i=0;i<8;i++){
642  emx=mx+dx[i];
643  emy=my+dy[i];
644  if ( (emx<0) || (emy<0) || (the_map.x<=emx) || (the_map.y<=emy)){
645  slevels[i]=0;
646  sfaces[i]=0; /*black picture*/
647  }
648  else if (the_map.cells[emx][emy].smooth[layer]<=the_map.cells[mx][my].smooth[layer]){
649  slevels[i]=0;
650  sfaces[i]=0; /*black picture*/
651  }else{
652  slevels[i]=the_map.cells[emx][emy].smooth[layer];
653  sfaces[i]=pixmaps[the_map.cells[emx][emy].heads[layer].face]->smooth_face;
654  }
655  }
656  /* ok, now we have a list of smoothlevel higher than current square.
657  * there are at most 8 different levels. so... let's check 8 times
658  * for the lowest one (we draw from botto to top!).
659  */
660  lowest=-1;
661  while (1){
662  lowest = -1;
663  for (i=0;i<8;i++){
664  if ( (slevels[i]>0) && (!partdone[i]) &&
665  ((lowest<0) || (slevels[i]<slevels[lowest]))
666  )
667  lowest=i;
668  }
669  if (lowest<0)
670  break; /*no more smooth to do on this square*/
671  /*printf ("hey, must smooth something...%d\n",sfaces[lowest]);*/
672  /*here we know 'what' to smooth*/
673  /* we need to calculate the weight
674  * for border and weight for corners.
675  * then we 'markdone'
676  * the corresponding squares
677  */
678  /*first, the border, which may exclude some corners*/
679  weight=0;
680  weightC=15; /*works in backward. remove where there is nothing*/
681  /*for (i=0;i<8;i++)
682  cornermask[i]=1;*/
683  for (i=0;i<8;i++){ /*check all nearby squares*/
684  if ( (slevels[i]==slevels[lowest]) &&
685  (sfaces[i]==sfaces[lowest])){
686  partdone[i]=1;
687  weight=weight+bweights[i];
688  weightC&=~bc_exclude[i];
689  }else{
690  /*must rmove the weight of a corner if not in smoothing*/
691  weightC&=~cweights[i];
692  }
693 
694  }
695  /*We can't do this before since we need the partdone to be adjusted*/
696  if (sfaces[lowest]<=0)
697  continue; /*Can't smooth black*/
698  smoothface=sfaces[lowest];
699  if (smoothface<=0){
700  continue; /*picture for smoothing not yet available*/
701  }
702  /* now, it's quite easy. We must draw using a 32x32 part of
703  * the picture smoothface.
704  * This part is located using the 2 weights calculated:
705  * (32*weight,0) and (32*weightC,32)
706  */
707  if ( (!pixmaps[smoothface]->map_image) ||
708  (pixmaps[smoothface] == pixmaps[0]))
709  continue; /*don't have the picture associated*/
710  if (weight>0){
711  src.x=map_image_size*weight;
712  src.y=0;
713  if (the_map.cells[mx][my].cleared) {
714  if (SDL_BlitSurface(pixmaps[smoothface]->fog_image,
715  &src, mapsurface, &dst))
716  do_SDL_error( "BlitSurface", __FILE__, __LINE__);
717  } else {
718  if (SDL_BlitSurface(pixmaps[smoothface]->map_image,
719  &src, mapsurface, &dst))
720  do_SDL_error( "BlitSurface", __FILE__, __LINE__);
721  }
722  }
723  if (weightC>0){
724  src.x=map_image_size*weightC;
725  src.y=map_image_size;
726  if (the_map.cells[mx][my].cleared) {
727  if (SDL_BlitSurface(pixmaps[smoothface]->fog_image,
728  &src, mapsurface, &dst))
729  do_SDL_error( "BlitSurface", __FILE__, __LINE__);
730  } else {
731  if (SDL_BlitSurface(pixmaps[smoothface]->map_image,
732  &src, mapsurface, &dst))
733  do_SDL_error( "BlitSurface", __FILE__, __LINE__);
734  }
735  }
736  }/*while there's some smooth to do*/
737 }
738 
739 /* This function tells if a specifi square need to be redrawn
740  * Reason for redrawing can be content change, smoothing change or
741  * surrounding lightning change. Conditions depend on type of
742  * lightning code used.
743  * Tchize 22 May 2004
744  */
745 static int sdl_square_need_redraw(int mx, int my){
746 #define SDL_LIGHT_CHANGED(_x_,_y_) (!mapdata_is_inside((_x_), (_y_)) ? 0 : the_map.cells[_x_][_y_].need_update)
747  if ( (the_map.cells[mx][my].need_update) || (the_map.cells[mx][my].need_resmooth))
748  return 1;
749 
751  /*The fast per pixel uses 4 additionnal datas which may have changed:*/
752  /*we suppose need_redraw -> lightr may have change. in future maybe we could add a need_relight toggle*/
753  if (SDL_LIGHT_CHANGED(mx-1,my) ||
754  SDL_LIGHT_CHANGED(mx,my-1) ||
755  SDL_LIGHT_CHANGED(mx+1,my) ||
756  SDL_LIGHT_CHANGED(mx,my+1))
757  return 1;
758  }
760  if (SDL_LIGHT_CHANGED(mx-1,my) ||
761  SDL_LIGHT_CHANGED(mx,my-1) ||
762  SDL_LIGHT_CHANGED(mx+1,my) ||
763  SDL_LIGHT_CHANGED(mx,my+1) ||
764  SDL_LIGHT_CHANGED(mx-1,my+1) ||
765  SDL_LIGHT_CHANGED(mx-1,my-1) ||
766  SDL_LIGHT_CHANGED(mx+1,my-1) ||
767  SDL_LIGHT_CHANGED(mx+1,my+1) )
768  return 1;
769  }
770  return 0; /*no need to redraw :)*/
771 }
772 
773 static void display_mapcell(int ax, int ay, int mx, int my)
774 {
775  SDL_Rect dst, src;
776  int layer;
777 
778  /* First, we need to black out this space. */
779  dst.x = ax*map_image_size;
780  dst.y = ay*map_image_size;
781  dst.w = map_image_size;
782  dst.h = map_image_size;
783  SDL_FillRect(mapsurface, &dst, SDL_MapRGB(mapsurface->format, 0, 0, 0));
784 
785  /* now draw the different layers. Only draw if using fog of war or the
786  * space isn't clear.
787  */
788  if (use_config[CONFIG_FOGWAR] || !the_map.cells[mx][my].cleared) {
789  for (layer=0; layer<MAXLAYERS; layer++) {
790  int sx, sy;
791 
792  /* draw single-tile faces first */
793  int face = mapdata_face(ax, ay, layer);
794  if (face > 0 && pixmaps[face]->map_image != NULL) {
795  int w = pixmaps[face]->map_width;
796  int h = pixmaps[face]->map_height;
797  /* add one to the size values to take into account the actual width of the space */
798  src.x = w-map_image_size;
799  src.y = h-map_image_size;
800  src.w = map_image_size;
801  src.h = map_image_size;
802  dst.x = ax*map_image_size;
803  dst.y = ay*map_image_size;
804  if (the_map.cells[mx][my].cleared) {
805  if (SDL_BlitSurface(pixmaps[face]->fog_image, &src, mapsurface, &dst))
806  do_SDL_error( "BlitSurface", __FILE__, __LINE__);
807  } else {
808  if (SDL_BlitSurface(pixmaps[face]->map_image, &src, mapsurface, &dst))
809  do_SDL_error( "BlitSurface", __FILE__, __LINE__);
810  }
811 
812  }
813  /* Sometimes, it may happens we need to draw the smooth while there
814  * is nothing to draw at that layer (but there was something at lower
815  * layers). This is handled here. The else part is to take into account
816  * cases where the smooth as already been handled 2 code lines before
817  */
819  drawsmooth_sdl (mx,my,layer,dst);
820 
821  /* draw big faces last (should overlap other objects) */
822  face = mapdata_bigface(ax, ay, layer, &sx, &sy);
823  if (face > 0 && pixmaps[face]->map_image != NULL) {
824  /* We have to handle images that are not an equal
825  * multiplier of map_image_size. See
826  * display_mapcell() in gtk-v2/src/map.c for
827  * more details on this logic, since it is basically
828  * the same.
829  */
830  int dx, dy, sourcex, sourcey, offx, offy;
831 
832  dx = pixmaps[face]->map_width % map_image_size;
833  offx = dx?(map_image_size -dx):0;
834 
835  if (sx) {
836  sourcex = sx * map_image_size - offx ;
837  offx=0;
838  } else {
839  sourcex=0;
840  }
841 
842  dy = pixmaps[face]->map_height % map_image_size;
843  offy = dy?(map_image_size -dy):0;
844 
845  if (sy) {
846  sourcey = sy * map_image_size - offy;
847  offy=0;
848  } else {
849  sourcey=0;
850  }
851 
852  src.x = sourcex;
853  src.y = sourcey;
854  src.w = map_image_size - offx;
855  src.h = map_image_size - offy;
856  dst.x = ax*map_image_size + offx;
857  dst.y = ay*map_image_size + offy;
858 
859  if (the_map.cells[mx][my].cleared) {
860  if (SDL_BlitSurface(pixmaps[face]->fog_image, &src, mapsurface, &dst))
861  do_SDL_error( "BlitSurface", __FILE__, __LINE__);
862  } else {
863  if (SDL_BlitSurface(pixmaps[face]->map_image, &src, mapsurface, &dst))
864  do_SDL_error( "BlitSurface", __FILE__, __LINE__);
865  }
866  } /* else for processing the layers */
867  }
868  }
869 
871  dst.x = ax*map_image_size;
872  dst.y = ay*map_image_size;
873  dst.w = map_image_size;
874  dst.h = map_image_size;
875 
876  /* Note - Instead of using a lightmap, I just fillrect
877  * directly onto the map surface - I would think this should be
878  * faster
879  */
880  if (the_map.cells[mx][my].darkness == 255) {
881  SDL_FillRect(mapsurface,&dst, SDL_MapRGB(mapsurface->format, 0, 0, 0));
882  } else if (the_map.cells[mx][my].darkness != 0) {
883  SDL_SetAlpha(lightmap, SDL_SRCALPHA|SDL_RLEACCEL, the_map.cells[mx][my].darkness);
884  SDL_BlitSurface(lightmap, NULL, mapsurface, &dst);
885  }
887  do_sdl_per_pixel_lighting(ax, ay, mx, my);
888  }
889 }
890 
891 /* This generates a map in SDL mode.
892  *
893  * I had to totally change the logic on how we do this in SDL mode -
894  * to support variable sized images, the old method of generating each
895  * space does not work, as one space may spill over to the other.
896  * Instead, we first blit the bottom layer, then the layer above
897  * that, and so on. This results in the map being drawn a bit
898  * more correctly. In fact, that logic actually isn't needed, as
899  * with the new map commands, we know the offset and size of the
900  * images.
901  *
902  * The logic here only redraws spaces that change. The logic in the
903  * common/commands.c files the odd layers with links for 'big images'.
904  * for objects on these layers, we look at the size_x and size_y values
905  * to determine the offset from which we should be blitting.
906  *
907  * Old notes, but left in:
908  * The performance here is very good in most cases - about 30 ms (on my system)
909  * is used just for my flip at the bottom of the function, drawing only what
910  * is needed generally saves a lot of time (<15 ms in most cases) compared to the
911  * 80-120 ms usually needed on a 15x15 map.
912  */
913 
914 void sdl_gen_map(int redraw) {
915  int mx, my, x, y;
916  struct timeval tv1, tv2,tv3;
917  long elapsed1, elapsed2;
918 
919  static int last_mapwidth=0;
920  static int last_mapheight=0;
921  static uint8 *redrawbitmap=NULL;
922 
923  if (time_map_redraw)
924  gettimeofday(&tv1, NULL);
925 
926  /* In per pixel lightning code, some square may have to be
927  * redrawn just because a surrounding square did change
928  * However, informations on changed square do get toggle
929  * during the redraw process. To keep track of which
930  * squares really need redraw (instead of redrawing eveything in
931  * per pixel lightning case), we need to save this info in a array
932  * Tchize, 22 May 2004
933  */
934  if ( (use_config[CONFIG_MAPWIDTH] != last_mapwidth) ||
935  (use_config[CONFIG_MAPHEIGHT] != last_mapheight) ){
936  /* reallocate array */
937  last_mapwidth=use_config[CONFIG_MAPWIDTH];
938  last_mapheight=use_config[CONFIG_MAPHEIGHT];
939  redrawbitmap=(uint8*)malloc(sizeof(uint8)*last_mapwidth*last_mapheight);
940  }
941  if (redrawbitmap==NULL){
942  LOG(LOG_ERROR,"sdl::sdl_gen_map",
943  "The redraw bitmap is NULL. Not enough memory? (width:%d,height:%d)",
944  last_mapwidth,last_mapheight);
945  return; /*without this bitmap, no drawing possible*/
946  }
947 
948  for( x= 0; x<use_config[CONFIG_MAPWIDTH]; x++) {
949  for(y = 0; y<use_config[CONFIG_MAPHEIGHT]; y++) {
950  redrawbitmap[x+y*last_mapwidth]=(uint8)sdl_square_need_redraw(x+pl_pos.x,y+pl_pos.y);
951  }
952  }
953  for( x= 0; x<use_config[CONFIG_MAPWIDTH]; x++) {
954  for(y = 0; y<use_config[CONFIG_MAPHEIGHT]; y++) {
955  /* mx,my represent the spaces on the 'virtual' map (ie, the_map structure).
956  * x and y (from the for loop) represent the visable screen.
957  */
958  mx = pl_pos.x+x;
959  my = pl_pos.y+y;
960  if (redraw || redrawbitmap[x+y*last_mapwidth]) {
961  display_mapcell(x, y, mx, my);
962  the_map.cells[mx][my].need_update = 0;
963  the_map.cells[mx][my].need_resmooth = 0;
964  }
965  }
966  }
967 
968  if (time_map_redraw)
969  gettimeofday(&tv2, NULL);
970 
971  SDL_Flip(mapsurface);
972 
973  if (time_map_redraw) {
974  gettimeofday(&tv3, NULL);
975  elapsed1 = (tv2.tv_sec - tv1.tv_sec)*1000000 + (tv2.tv_usec - tv1.tv_usec);
976  elapsed2 = (tv3.tv_sec - tv2.tv_sec)*1000000 + (tv3.tv_usec - tv2.tv_usec);
977 
978  /* I care about performance for 'long' updates, so put the check in to make
979  * these a little more noticable */
980  if ((elapsed1 + elapsed2)>10000)
981  LOG(LOG_INFO,"gtk::sdl_gen_map","gen took %7ld, flip took %7ld, total = %7ld",
982  elapsed1, elapsed2, elapsed1 + elapsed2);
983  }
984 } /* sdl_gen_map function */
985 
986 int sdl_mapscroll(int dx, int dy)
987 {
988  /* a copy of what pngximage does except sdl specfic
989  * mapsurface->pitch is the length of a scanline in bytes
990  * including alignment padding
991  */
992  SDL_LockSurface( mapsurface);
993  if( dy < 0) {
994  int offset= mapsurface->pitch * (-dy*map_image_size);
995  memmove( mapsurface->pixels + offset, mapsurface->pixels,
996  mapsurface->pitch * (mapsurface->h + dy*map_image_size) );
997  }
998  else if( dy > 0) {
999  int offset= mapsurface->pitch * (dy*map_image_size);
1000  memmove( mapsurface->pixels, mapsurface->pixels + offset,
1001  mapsurface->pitch * (mapsurface->h - dy*map_image_size) );
1002  }
1003 
1004  if (dx) {
1005  int y;
1006  for( y= 0; y < mapsurface->h; y++) {
1007  if( dx < 0) {
1008  char* start_of_row= mapsurface->pixels + mapsurface->pitch * y;
1009  int offset= ( mapsurface->format->BytesPerPixel * map_image_size * -dx);
1010  memmove( start_of_row + offset, start_of_row,
1011  mapsurface->pitch - offset);
1012  }
1013  else {
1014  char* start_of_row= mapsurface->pixels + mapsurface->pitch * y;
1015  int offset= ( mapsurface->format->BytesPerPixel * map_image_size * dx);
1016  memmove( start_of_row, start_of_row + offset,
1017  mapsurface->pitch - offset);
1018  }
1019  }
1020  }
1021  SDL_UnlockSurface( mapsurface);
1022 
1023  return 1;
1024 }
1025 
1026 #endif
#define CAN_SMOOTH(__SQUARE, __LEVEL)
Definition: client.h:85
static int height
Definition: mapdata.c:104
int y
Definition: mapdata.h:93
void init_SDL(GtkWidget *sdl_window, int just_lightmap)
int map_image_half_size
Definition: gx11.c:117
void * fog_image
Definition: gx11.h:65
#define CONFIG_FOGWAR
Definition: client.h:157
uint16 map_height
Definition: gx11.h:64
#define CFG_LT_PIXEL_BEST
Definition: client.h:189
uint8 have_darkness
Definition: mapdata.h:82
PixmapInfo * pixmaps[MAXPIXMAPNUM]
Definition: gx11.c:118
uint8 need_update
Definition: mapdata.h:81
void * map_image
Definition: gx11.h:63
sint16 face
Definition: mapdata.h:45
static int width
Definition: mapdata.c:104
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
#define TRUE
Definition: client-types.h:71
int map_image_size
Definition: gx11.c:117
int sdl_mapscroll(int dx, int dy)
const char *const rcsid_gtk_sdl_c
Definition: sdl.c:1
static void display_mapcell(int ax, int ay, int mx, int my)
Definition: map.c:219
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
sint16 mapdata_face(int x, int y, int layer)
Definition: mapdata.c:955
#define CFG_LT_TILE
Definition: client.h:187
uint8 darkness
Definition: mapdata.h:80
#define CFG_LT_PIXEL
Definition: client.h:188
uint8 cleared
Definition: mapdata.h:84
struct Map the_map
Definition: mapdata.c:121
struct MapCell ** cells
Definition: mapdata.h:95
int x
Definition: mapdata.h:92
#define CONFIG_SMOOTH
Definition: client.h:177
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)
uint8 need_resmooth
Definition: mapdata.h:83
GdkBitmap * dark2
Definition: gx11.c:249
GdkBitmap * dark3
Definition: gx11.c:249
#define MAXLAYERS
Definition: mapdata.h:32
#define CONFIG_MAPWIDTH
Definition: client.h:170
#define CONFIG_SHOWGRID
Definition: client.h:167
GdkBitmap * dark1
Definition: gx11.c:249
uint16 smooth[MAXLAYERS]
Definition: mapdata.h:79
#define CONFIG_MAPHEIGHT
Definition: client.h:171
sint16 mapdata_bigface(int x, int y, int layer, int *ww, int *hh)
Definition: mapdata.c:966
uint16 smooth_face
Definition: gx11.h:66
unsigned char uint8
Definition: client-types.h:81
struct MapCellLayer heads[MAXLAYERS]
Definition: mapdata.h:77
#define CONFIG_LIGHTING
Definition: client.h:168
uint8 time_map_redraw
Definition: gx11.c:203
void sdl_gen_map(int redraw)
PlayerPosition pl_pos
Definition: map.c:69
uint16 map_width
Definition: gx11.h:64