Crossfire Client, Trunk  R20996
image.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, see the
9  * 'LICENSE' and 'COPYING' files.
10  *
11  * The authors can be reached via e-mail to crossfire-devel@real-time.com
12  */
13 
21 #include "client.h"
22 
23 #include <gtk/gtk.h>
24 
25 #ifdef HAVE_SDL
26 #include <SDL/SDL.h>
27 #include <SDL/SDL_image.h>
28 #endif
29 
30 #include "image.h"
31 #include "main.h"
32 #include "gtk2proto.h"
33 
34 extern GtkWidget *window_root;
36 
37 #define BPP 4
38 
40 
41 /* Do we have new images to display? */
43 
44 /*
45  * this is used to rescale big images that will be drawn in the inventory/look
46  * lists. What the code further below basically does is figure out how big the
47  * object is (in squares), and this looks at the icon_rescale_factor to figure
48  * what scale factor it gives. Not that the icon_rescale_factor values are
49  * passed directly to the rescale routines. These represent percentages - so
50  * even taking into account that the values diminish as the table grows, they
51  * will still appear larger if the location in the table times the factor is
52  * greater than 100. We find the largest dimension that the image has. The
53  * values in the comment is the effective scaling compared to the base image
54  * size that this big image will appear as. Using a table makes it easier to
55  * adjust the values so things look right.
56  */
57 
58 #define MAX_ICON_SPACES 10
59 static const int icon_rescale_factor[MAX_ICON_SPACES] = {
60  100, 100, 80 /* 2 = 160 */, 60 /* 3 = 180 */,
61  50 /* 4 = 200 */, 45 /* 5 = 225 */, 40 /* 6 = 240 */,
62  35 /* 7 = 259 */, 35 /* 8 = 280 */, 33 /* 9 = 300 */
63 };
64 
65 /******************************************************************************
66  *
67  * Code related to face caching.
68  *
69  *****************************************************************************/
70 
71 /* Does not appear to be used anywhere
72 typedef struct Keys {
73  uint8 flags;
74  sint8 direction;
75  KeySym keysym;
76  char *command;
77  struct Keys *next;
78 } Key_Entry;
79 */
80 
81 /* Rotate right from bsd sum. */
82 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1;
83 
84 /*#define CHECKSUM_DEBUG*/
85 
89 static void create_icon_image(guint8 *data, PixmapInfo *pi) {
90  pi->icon_mask = NULL;
91  pi->icon_image = rgba_to_gdkpixbuf(data, pi->icon_width, pi->icon_height);
92 }
93 
100 static void create_map_image(guint8 *data, PixmapInfo *pi) {
101  pi->map_image = NULL;
102  pi->map_mask = NULL;
103 
105 #if defined(HAVE_SDL)
106  int i;
107  SDL_Surface *fog;
108  guint32 g,*p;
109  guint8 *l;
110 
111 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
112  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
113  pi->map_height, 32, pi->map_width * 4, 0xff,
114  0xff00, 0xff0000, 0xff000000);
115 
116  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
117  pi->map_width, pi->map_height, 32, 0xff,
118  0xff00, 0xff0000, 0xff000000);
119  SDL_LockSurface(fog);
120 
121  for (i=0; i < pi->map_width * pi->map_height; i++) {
122  l = (guint8 *) (data + i*4);
123 #if 1
124  g = MAX(*l, *(l+1));
125  g = MAX(g, *(l+2));
126 #else
127  g = ( *l + *(l+1) + *(l+2)) / 3;
128 #endif
129  p = (guint32*) fog->pixels + i;
130  *p = g | (g << 8) | (g << 16) | (*(l + 3) << 24);
131  }
132 
133  SDL_UnlockSurface(fog);
134  pi->fog_image = fog;
135 #else
136  /* Big endian */
137  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
138  pi->map_height, 32, pi->map_width * 4, 0xff000000,
139  0xff0000, 0xff00, 0xff);
140 
141  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
142  pi->map_width, pi->map_height, 32, 0xff000000,
143  0xff0000, 0xff00, 0xff);
144  SDL_LockSurface(fog);
145 
146  /*
147  * I think this works out, but haven't tried it on a big endian machine
148  * as my recollection is that the png data would be in the same order,
149  * just the bytes for it to go on the screen are reversed.
150  */
151  for (i=0; i < pi->map_width * pi->map_height; i++) {
152  l = (guint8 *) (data + i*4);
153 #if 1
154  g = MAX(*l, *(l+1));
155  g = MAX(g, *(l+2));
156 #else
157  g = ( *l + *(l+1) + *(l+2)) / 3;
158 #endif
159  p = (guint32*) fog->pixels + i;
160  *p = (g << 8) | (g << 16) | (g << 24) | *(l + 3);
161  }
162 
163  for (i=0; i < pi->map_width * pi->map_height; i+= 4) {
164  guint32 *tmp;
165 
166  /*
167  * The pointer arithemtic below looks suspicious, but it is a patch
168  * that is submitted, so just putting it in as submitted. MSW
169  * 2004-05-11
170  */
171  p = (guint32*) (fog->pixels + i);
172  g = ( ((*p >> 24) & 0xff) + ((*p >> 16) & 0xff) + ((*p >> 8) & 0xff)) / 3;
173  tmp = (guint32*) fog->pixels + i;
174  *tmp = (g << 24) | (g << 16) | (g << 8) | (*p & 0xff);
175  }
176 
177  SDL_UnlockSurface(fog);
178  pi->fog_image = fog;
179 #endif
180 
181 #endif
183 #ifdef HAVE_OPENGL
184  create_opengl_map_image(data, pi);
185 #endif
187  pi->map_image = rgba_to_cairo_surface(data, pi->map_width, pi->map_height);
188  }
189 }
190 
196 static void free_pixmap(PixmapInfo *pi)
197 {
198  if (pi->icon_image) {
199  g_object_unref(pi->icon_image);
200  }
201  if (pi->icon_mask) {
202  g_object_unref(pi->icon_mask);
203  }
204  if (pi->map_mask) {
205  g_object_unref(pi->map_mask);
206  }
208 #ifdef HAVE_SDL
209  if (pi->map_image) {
210  g_free(((SDL_Surface*)pi->map_image)->pixels);
211  SDL_FreeSurface(pi->map_image);
212  SDL_FreeSurface(pi->fog_image);
213  /*
214  * Minor memory leak here - SDL_FreeSurface() frees the pixel
215  * data _unless_ SDL_CreateRGBSurfaceFrom() was used to create
216  * the surface. SDL_CreateRGBSurfaceFrom() is used to create
217  * the map data, which is why we need the free there. The
218  * reason this is a minor memory leak is because
219  * SDL_CreateRGBSurfaceFrom() is used to create the question
220  * mark image, and without this free, that data is not freed.
221  * However, with this, client crashes after disconnecting from
222  * server with double free.
223  */
224  /* free(((SDL_Surface*)pi->fog_image)->pixels);*/
225  }
226 #endif
228 #ifdef HAVE_OPENGL
229  opengl_free_pixmap(pi);
230 #endif
232  if (pi->map_image) {
233  cairo_surface_destroy(pi->map_image);
234  }
235  }
236 }
237 
252  guint8 *rgba_data, int width, int height) {
253  int nx, ny, iscale, factor;
254  PixmapInfo *pi;
255 
256  if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM) {
257  return 1;
258  }
259 
260  if (pixmaps[pixmap_num] != pixmaps[0]) {
261  /* As per bug 2938906, one can see image corruption when switching between
262  * servers. The cause is that the cache table stores away
263  * a pointer to the pixmap[] entry - if we go and free it,
264  * the cache table can point to garbage, so don't free it.
265  * This causes some memory leak, but if/when there is good
266  * cache support for multiple servers, eventually the amount
267  * of memory consumed will reach a limit (it has every image of
268  * every server in memory
269  *
270  * The cause of image corruption requires a few different things:
271  * 1) images of the same name have different numbers on the 2 serves.
272  * 2) the image number is higher on the first than second server
273  * 3) the image using the high number does not exist/is different
274  * on the second server, causing this routine to be called.
275  */
276 
277  if (!use_config[CONFIG_CACHE]) {
278  free_pixmap(pixmaps[pixmap_num]);
279  free(pixmaps[pixmap_num]);
280  }
281  pixmaps[pixmap_num] = pixmaps[0];
282  }
283 
284  pi = calloc(1, sizeof(PixmapInfo));
285 
286  iscale = use_config[CONFIG_ICONSCALE];
287 
288  /*
289  * If the image is big, figure out what we should scale it to so it fits
290  * better display
291  */
292  if (width > DEFAULT_IMAGE_SIZE || height>DEFAULT_IMAGE_SIZE) {
293  int ts = 100;
294 
295  factor = width / DEFAULT_IMAGE_SIZE;
296  if (factor >= MAX_ICON_SPACES) {
297  factor = MAX_ICON_SPACES - 1;
298  }
299  if (icon_rescale_factor[factor] < ts) {
300  ts = icon_rescale_factor[factor];
301  }
302 
303  factor = height / DEFAULT_IMAGE_SIZE;
304  if (factor >= MAX_ICON_SPACES) {
305  factor = MAX_ICON_SPACES - 1;
306  }
307  if (icon_rescale_factor[factor] < ts) {
308  ts = icon_rescale_factor[factor];
309  }
310 
311  iscale = ts * use_config[CONFIG_ICONSCALE] / 100;
312  }
313 
314  /* In all cases, the icon images are in native form. */
315  if (iscale != 100) {
316  nx=width;
317  ny=height;
318  guint8 *png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, iscale);
319  pi->icon_width = nx;
320  pi->icon_height = ny;
321  create_icon_image(png_tmp, pi);
322  free(png_tmp);
323  } else {
324  pi->icon_width = width;
325  pi->icon_height = height;
326  create_icon_image(rgba_data, pi);
327  }
328 
329  /*
330  * If icon_scale matched use_config[CONFIG_MAPSCALE], we could try to be
331  * more intelligent, but this should not be called too often, and this
332  * keeps the code simpler.
333  */
334  guint8 *png_tmp;
335 
336  if (use_config[CONFIG_MAPSCALE] != 100) {
337  nx=width;
338  ny=height;
339  png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, use_config[CONFIG_MAPSCALE]);
340  pi->map_width = nx;
341  pi->map_height = ny;
342  create_map_image(png_tmp, pi);
343  /*
344  * pixmap mode and opengl don't need the rgba data after they have
345  * created the image, so we can free it. SDL uses the raw rgba data,
346  * so it can't be freed.
347  */
350  free(png_tmp);
351  }
352  } else {
353  pi->map_width = width;
354  pi->map_height = height;
355  /*
356  * If using SDL mode, a copy of the rgba data needs to be stored away.
357  */
359  png_tmp = g_malloc(width * height * BPP);
360  memcpy(png_tmp, rgba_data, width * height * BPP);
361  } else {
362  png_tmp = rgba_data;
363  }
364  create_map_image(png_tmp, pi);
365  }
366  /*
367  * Not ideal, but if it is missing the map or icon image, presume something
368  * failed. However, opengl doesn't set the map_image, so if using that
369  * display mode, don't make this check.
370  */
372  free_pixmap(pi);
373  free(pi);
374  return 1;
375  }
376  if (ce) {
377  ce->image_data = pi;
378  }
379  pixmaps[pixmap_num] = pi;
380  if (use_config[CONFIG_CACHE]) {
381  have_new_image++;
382  }
383 
384  return 0;
385 }
386 
393 void addsmooth(guint16 face, guint16 smooth_face)
394 {
395  pixmaps[face]->smooth_face = smooth_face;
396 }
397 
406 int associate_cache_entry(Cache_Entry *ce, int pixnum)
407 {
408  pixmaps[pixnum] = ce->image_data;
409  return 0;
410 }
411 
419 {
420  int i;
421 
423  /*
424  * The entries in the pixmaps array are also tracked in the image cache in
425  * the common area. We will try to recycle those images that we can.
426  * Thus, if we connect to a new server, we can just re-use the images we
427  * have already rendered.
428  */
429  for (i=1; i<MAXPIXMAPNUM; i++) {
430  if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) {
431  free_pixmap(pixmaps[i]);
432  free(pixmaps[i]);
433  pixmaps[i] = pixmaps[0];
434  }
435  }
436 }
437 
438 static GtkWidget *pbar, *pbar_window;
439 
451 void image_update_download_status(int start, int end, int total) {
452  int x, y, wx, wy, w, h;
453 
454  if (start == 1) {
455  pbar = gtk_progress_bar_new();
456  get_window_coord(window_root, &x,&y, &wx,&wy,&w,&h);
457 
458  pbar_window = gtk_window_new(GTK_WINDOW_POPUP);
459  gtk_window_set_transient_for(GTK_WINDOW(pbar_window), GTK_WINDOW (window_root));
460 
461  gtk_container_add(GTK_CONTAINER(pbar_window), pbar);
462  gtk_widget_show(pbar);
463  gtk_widget_show(pbar_window);
464  } else if (start == total) {
465  gtk_widget_destroy(pbar_window);
466  pbar = NULL;
467  pbar_window = NULL;
468  return;
469  }
470 
471  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pbar), (float)start / end);
472  while (gtk_events_pending()) {
473  gtk_main_iteration();
474  }
475 }
476 
483 void get_map_image_size(int face, guint8 *w, guint8 *h)
484 {
485  /* We want to calculate the number of spaces this image
486  * uses it. By adding the image size but substracting one,
487  * we cover the cases where the image size is not an even
488  * increment. EG, if the map_image_size is 32, and an image
489  * is 33 wide, we want that to register as two spaces. By
490  * adding 31, that works out.
491  */
492  if ( face < 0 || face >= MAXPIXMAPNUM) {
493  *w = 1;
494  *h = 1;
495  } else {
496  *w = (pixmaps[face]->map_width + map_image_size - 1)/ map_image_size;
497  *h = (pixmaps[face]->map_height + map_image_size - 1)/ map_image_size;
498  }
499 }
500 
501 /******************************************************************************
502  *
503  * Code related to face caching.
504  *
505  *****************************************************************************/
506 
517 {
518  int i;
519  GtkStyle *style;
520 #include "../../pixmaps/question.xpm"
521 
522  style = gtk_widget_get_style(window_root);
523  pixmaps[0] = g_new(PixmapInfo, 1);
524  pixmaps[0]->icon_image =
525  gdk_pixbuf_new_from_xpm_data((const gchar **)question_xpm);
526 #ifdef HAVE_SDL
528  /*
529  * Make a semi-transparent question mark symbol to use for the cached
530  * images.
531  */
532 #include "../../pixmaps/question.sdl"
533  pixmaps[0]->map_image = SDL_CreateRGBSurfaceFrom(question_sdl,
534  32, 32, 1, 4, 1, 1, 1, 1);
535  SDL_SetAlpha(pixmaps[0]->map_image, SDL_SRCALPHA, 70);
536  pixmaps[0]->fog_image = SDL_CreateRGBSurfaceFrom(question_sdl,
537  32, 32, 1, 4, 1, 1, 1, 1);
538  SDL_SetAlpha(pixmaps[0]->fog_image, SDL_SRCALPHA, 70);
539  } else
540 #endif
542  pixmaps[0]->map_image = pixmaps[0]->icon_image;
543  pixmaps[0]->fog_image = pixmaps[0]->icon_image;
544  pixmaps[0]->map_mask = pixmaps[0]->icon_mask;
545  }
546 #ifdef HAVE_OPENGL
549  }
550 #endif
551 
552  pixmaps[0]->icon_width = pixmaps[0]->icon_height = pixmaps[0]->map_width = pixmaps[0]->map_height = map_image_size;
553  pixmaps[0]->smooth_face = 0;
554 
555  /* Don't do anything special for SDL image - rather, that drawing
556  * code will check to see if there is no data
557  */
558 
559  /* Initialize all the images to be of the same value. */
560  for (i=1; i<MAXPIXMAPNUM; i++) {
561  pixmaps[i] = pixmaps[0];
562  }
563 
565 }
void get_window_coord(GtkWidget *win, int *x, int *y, int *wx, int *wy, int *w, int *h)
Definition: main.c:527
static int height
Definition: mapdata.c:80
#define BPP
Definition: image.c:37
int image_size
Definition: image.c:35
void addsmooth(guint16 face, guint16 smooth_face)
Definition: image.c:393
gint16 use_config[CONFIG_NUMS]
Definition: init.c:40
#define DEFAULT_IMAGE_SIZE
Definition: image.h:40
static const int icon_rescale_factor[MAX_ICON_SPACES]
Definition: image.c:59
int map_image_size
Definition: map.c:39
void opengl_free_pixmap(PixmapInfo *pi)
#define CONFIG_DISPLAYMODE
Definition: client.h:194
void * fog_image
Definition: image.h:53
#define MAXPIXMAPNUM
Definition: client.h:498
static GtkWidget * pbar_window
Definition: image.c:438
void * image_data
Definition: client.h:511
void image_update_download_status(int start, int end, int total)
Definition: image.c:451
void * map_mask
Definition: image.h:51
void create_opengl_map_image(guint8 *data, PixmapInfo *pi)
void * map_image
Definition: image.h:51
guint16 icon_height
Definition: image.h:50
int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, guint8 *rgba_data, int width, int height)
Definition: image.c:251
static int width
Definition: mapdata.c:79
#define CFG_DM_PIXMAP
Definition: client.h:239
void init_image_cache_data(void)
Definition: image.c:516
guint16 smooth_face
Definition: image.h:54
GtkWidget * window_root
Definition: main.c:97
void create_opengl_question_mark(void)
guint16 map_height
Definition: image.h:52
void reset_image_cache_data(void)
Definition: image.c:509
#define CONFIG_CACHE
Definition: client.h:189
guint16 map_width
Definition: image.h:52
gint16 want_config[CONFIG_NUMS]
Definition: init.c:40
#define MAX_ICON_SPACES
Definition: image.c:58
static void create_map_image(guint8 *data, PixmapInfo *pi)
Definition: image.c:100
int have_new_image
Definition: image.c:42
cairo_surface_t * rgba_to_cairo_surface(guint8 *data, int width, int height)
Definition: png.c:440
static void create_icon_image(guint8 *data, PixmapInfo *pi)
Definition: image.c:89
void reset_image_data(void)
Definition: image.c:418
#define CONFIG_ICONSCALE
Definition: client.h:191
static void free_pixmap(PixmapInfo *pi)
Definition: image.c:196
#define CFG_DM_SDL
Definition: client.h:240
guint16 icon_width
Definition: image.h:50
Definition: client.h:507
PixmapInfo * pixmaps[MAXPIXMAPNUM]
Definition: image.c:39
void * icon_mask
Definition: image.h:48
void init_common_cache_data(void)
Definition: image.c:359
guint8 * rescale_rgba_data(guint8 *data, int *width, int *height, int scale)
Definition: png.c:235
static GtkWidget * pbar
Definition: image.c:438
GdkPixbuf * rgba_to_gdkpixbuf(guint8 *data, int width, int height)
Definition: png.c:414
#define CFG_DM_OPENGL
Definition: client.h:241
GdkPixbuf * icon_image
Definition: image.h:49
int associate_cache_entry(Cache_Entry *ce, int pixnum)
Definition: image.c:406
#define CONFIG_MAPSCALE
Definition: client.h:192
void get_map_image_size(int face, guint8 *w, guint8 *h)
Definition: image.c:483