Crossfire Client, Trunk  R19900
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.h>
27 #include <SDL_image.h>
28 #endif
29 
30 #include "image.h"
31 #include "main.h"
32 #include "mapdata.h"
33 #include "gtk2proto.h"
34 
35 extern GtkWidget *window_root;
37 
38 struct {
39  char *name;
40  guint32 checksum;
41  guint8 *png_data;
42  guint32 width, height;
44 
45 #define BPP 4
46 
48 
50 
51 /* Do we have new images to display? */
53 
54 /*
55  * this is used to rescale big images that will be drawn in the inventory/look
56  * lists. What the code further below basically does is figure out how big the
57  * object is (in squares), and this looks at the icon_rescale_factor to figure
58  * what scale factor it gives. Not that the icon_rescale_factor values are
59  * passed directly to the rescale routines. These represent percentages - so
60  * even taking into account that the values diminish as the table grows, they
61  * will still appear larger if the location in the table times the factor is
62  * greater than 100. We find the largest dimension that the image has. The
63  * values in the comment is the effective scaling compared to the base image
64  * size that this big image will appear as. Using a table makes it easier to
65  * adjust the values so things look right.
66  */
67 
68 #define MAX_ICON_SPACES 10
69 static const int icon_rescale_factor[MAX_ICON_SPACES] = {
70  100, 100, 80 /* 2 = 160 */, 60 /* 3 = 180 */,
71  50 /* 4 = 200 */, 45 /* 5 = 225 */, 40 /* 6 = 240 */,
72  35 /* 7 = 259 */, 35 /* 8 = 280 */, 33 /* 9 = 300 */
73 };
74 
75 /******************************************************************************
76  *
77  * Code related to face caching.
78  *
79  *****************************************************************************/
80 
81 /* Does not appear to be used anywhere
82 typedef struct Keys {
83  uint8 flags;
84  sint8 direction;
85  KeySym keysym;
86  char *command;
87  struct Keys *next;
88 } Key_Entry;
89 */
90 
91 /* Rotate right from bsd sum. */
92 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1;
93 
94 /*#define CHECKSUM_DEBUG*/
95 
99 static void create_icon_image(guint8 *data, PixmapInfo *pi) {
100  pi->icon_mask = NULL;
101  pi->icon_image = rgba_to_gdkpixbuf(data, pi->icon_width, pi->icon_height);
102 }
103 
110 static void create_map_image(guint8 *data, PixmapInfo *pi) {
111  pi->map_image = NULL;
112  pi->map_mask = NULL;
113 
115 #if defined(HAVE_SDL)
116  int i;
117  SDL_Surface *fog;
118  guint32 g,*p;
119  guint8 *l;
120 
121 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
122  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
123  pi->map_height, 32, pi->map_width * 4, 0xff,
124  0xff00, 0xff0000, 0xff000000);
125 
126  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
127  pi->map_width, pi->map_height, 32, 0xff,
128  0xff00, 0xff0000, 0xff000000);
129  SDL_LockSurface(fog);
130 
131  for (i=0; i < pi->map_width * pi->map_height; i++) {
132  l = (guint8 *) (data + i*4);
133 #if 1
134  g = MAX(*l, *(l+1));
135  g = MAX(g, *(l+2));
136 #else
137  g = ( *l + *(l+1) + *(l+2)) / 3;
138 #endif
139  p = (guint32*) fog->pixels + i;
140  *p = g | (g << 8) | (g << 16) | (*(l + 3) << 24);
141  }
142 
143  SDL_UnlockSurface(fog);
144  pi->fog_image = fog;
145 #else
146  /* Big endian */
147  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
148  pi->map_height, 32, pi->map_width * 4, 0xff000000,
149  0xff0000, 0xff00, 0xff);
150 
151  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
152  pi->map_width, pi->map_height, 32, 0xff000000,
153  0xff0000, 0xff00, 0xff);
154  SDL_LockSurface(fog);
155 
156  /*
157  * I think this works out, but haven't tried it on a big endian machine
158  * as my recollection is that the png data would be in the same order,
159  * just the bytes for it to go on the screen are reversed.
160  */
161  for (i=0; i < pi->map_width * pi->map_height; i++) {
162  l = (guint8 *) (data + i*4);
163 #if 1
164  g = MAX(*l, *(l+1));
165  g = MAX(g, *(l+2));
166 #else
167  g = ( *l + *(l+1) + *(l+2)) / 3;
168 #endif
169  p = (guint32*) fog->pixels + i;
170  *p = (g << 8) | (g << 16) | (g << 24) | *(l + 3);
171  }
172 
173  for (i=0; i < pi->map_width * pi->map_height; i+= 4) {
174  guint32 *tmp;
175 
176  /*
177  * The pointer arithemtic below looks suspicious, but it is a patch
178  * that is submitted, so just putting it in as submitted. MSW
179  * 2004-05-11
180  */
181  p = (guint32*) (fog->pixels + i);
182  g = ( ((*p >> 24) & 0xff) + ((*p >> 16) & 0xff) + ((*p >> 8) & 0xff)) / 3;
183  tmp = (guint32*) fog->pixels + i;
184  *tmp = (g << 24) | (g << 16) | (g << 8) | (*p & 0xff);
185  }
186 
187  SDL_UnlockSurface(fog);
188  pi->fog_image = fog;
189 #endif
190 
191 #endif
193 #ifdef HAVE_OPENGL
194  create_opengl_map_image(data, pi);
195 #endif
197  pi->map_image = rgba_to_cairo_surface(data, pi->map_width, pi->map_height);
198  }
199 }
200 
206 static void free_pixmap(PixmapInfo *pi)
207 {
208  if (pi->icon_image) {
209  g_object_unref(pi->icon_image);
210  }
211  if (pi->icon_mask) {
212  g_object_unref(pi->icon_mask);
213  }
214  if (pi->map_mask) {
215  gdk_pixmap_unref(pi->map_mask);
216  }
218 #ifdef HAVE_SDL
219  if (pi->map_image) {
220  SDL_FreeSurface(pi->map_image);
221  free(((SDL_Surface*)pi->map_image)->pixels);
222  SDL_FreeSurface(pi->fog_image);
223  /*
224  * Minor memory leak here - SDL_FreeSurface() frees the pixel
225  * data _unless_ SDL_CreateRGBSurfaceFrom() was used to create
226  * the surface. SDL_CreateRGBSurfaceFrom() is used to create
227  * the map data, which is why we need the free there. The
228  * reason this is a minor memory leak is because
229  * SDL_CreateRGBSurfaceFrom() is used to create the question
230  * mark image, and without this free, that data is not freed.
231  * However, with this, client crashes after disconnecting from
232  * server with double free.
233  */
234  /* free(((SDL_Surface*)pi->fog_image)->pixels);*/
235  }
236 #endif
238 #ifdef HAVE_OPENGL
239  opengl_free_pixmap(pi);
240 #endif
242  if (pi->map_image) {
243  cairo_surface_destroy(pi->map_image);
244  }
245  }
246 }
247 
262  guint8 *rgba_data, int width, int height) {
263  int nx, ny, iscale, factor;
264  PixmapInfo *pi;
265 
266  if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM) {
267  return 1;
268  }
269 
270  if (pixmaps[pixmap_num] != pixmaps[0]) {
271  /* As per bug 2938906, one can see image corruption when switching between
272  * servers. The cause is that the cache table stores away
273  * a pointer to the pixmap[] entry - if we go and free it,
274  * the cache table can point to garbage, so don't free it.
275  * This causes some memory leak, but if/when there is good
276  * cache support for multiple servers, eventually the amount
277  * of memory consumed will reach a limit (it has every image of
278  * every server in memory
279  *
280  * The cause of image corruption requires a few different things:
281  * 1) images of the same name have different numbers on the 2 serves.
282  * 2) the image number is higher on the first than second server
283  * 3) the image using the high number does not exist/is different
284  * on the second server, causing this routine to be called.
285  */
286 
287  if (!use_config[CONFIG_CACHE]) {
288  free_pixmap(pixmaps[pixmap_num]);
289  free(pixmaps[pixmap_num]);
290  }
291  pixmaps[pixmap_num] = pixmaps[0];
292  }
293 
294  pi = calloc(1, sizeof(PixmapInfo));
295 
296  iscale = use_config[CONFIG_ICONSCALE];
297 
298  /*
299  * If the image is big, figure out what we should scale it to so it fits
300  * better display
301  */
302  if (width > DEFAULT_IMAGE_SIZE || height>DEFAULT_IMAGE_SIZE) {
303  int ts = 100;
304 
305  factor = width / DEFAULT_IMAGE_SIZE;
306  if (factor >= MAX_ICON_SPACES) {
307  factor = MAX_ICON_SPACES - 1;
308  }
309  if (icon_rescale_factor[factor] < ts) {
310  ts = icon_rescale_factor[factor];
311  }
312 
313  factor = height / DEFAULT_IMAGE_SIZE;
314  if (factor >= MAX_ICON_SPACES) {
315  factor = MAX_ICON_SPACES - 1;
316  }
317  if (icon_rescale_factor[factor] < ts) {
318  ts = icon_rescale_factor[factor];
319  }
320 
321  iscale = ts * use_config[CONFIG_ICONSCALE] / 100;
322  }
323 
324  /* In all cases, the icon images are in native form. */
325  if (iscale != 100) {
326  nx=width;
327  ny=height;
328  guint8 *png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, iscale);
329  pi->icon_width = nx;
330  pi->icon_height = ny;
331  create_icon_image(png_tmp, pi);
332  free(png_tmp);
333  } else {
334  pi->icon_width = width;
335  pi->icon_height = height;
336  create_icon_image(rgba_data, pi);
337  }
338 
339  /*
340  * If icon_scale matched use_config[CONFIG_MAPSCALE], we could try to be
341  * more intelligent, but this should not be called too often, and this
342  * keeps the code simpler.
343  */
344  guint8 *png_tmp;
345 
346  if (use_config[CONFIG_MAPSCALE] != 100) {
347  nx=width;
348  ny=height;
349  png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, use_config[CONFIG_MAPSCALE]);
350  pi->map_width = nx;
351  pi->map_height = ny;
352  create_map_image(png_tmp, pi);
353  /*
354  * pixmap mode and opengl don't need the rgba data after they have
355  * created the image, so we can free it. SDL uses the raw rgba data,
356  * so it can't be freed.
357  */
360  free(png_tmp);
361  }
362  } else {
363  pi->map_width = width;
364  pi->map_height = height;
365  /*
366  * If using SDL mode, a copy of the rgba data needs to be stored away.
367  */
369  png_tmp = g_malloc(width * height * BPP);
370  memcpy(png_tmp, rgba_data, width * height * BPP);
371  } else {
372  png_tmp = rgba_data;
373  }
374  create_map_image(png_tmp, pi);
375  }
376  /*
377  * Not ideal, but if it is missing the map or icon image, presume something
378  * failed. However, opengl doesn't set the map_image, so if using that
379  * display mode, don't make this check.
380  */
382  free_pixmap(pi);
383  free(pi);
384  return 1;
385  }
386  if (ce) {
387  ce->image_data = pi;
388  }
389  pixmaps[pixmap_num] = pi;
390  if (use_config[CONFIG_CACHE]) {
391  have_new_image++;
392  }
393 
394  return 0;
395 }
396 
403 void addsmooth(guint16 face, guint16 smooth_face)
404 {
405  pixmaps[face]->smooth_face = smooth_face;
406 }
407 
416 int associate_cache_entry(Cache_Entry *ce, int pixnum)
417 {
418  pixmaps[pixnum] = ce->image_data;
419  return 0;
420 }
421 
429 {
430  int i;
431 
433  /*
434  * The entries in the pixmaps array are also tracked in the image cache in
435  * the common area. We will try to recycle those images that we can.
436  * Thus, if we connect to a new server, we can just re-use the images we
437  * have already rendered.
438  */
439  for (i=1; i<MAXPIXMAPNUM; i++) {
440  if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) {
441  free_pixmap(pixmaps[i]);
442  free(pixmaps[i]);
443  pixmaps[i] = pixmaps[0];
444  }
445  }
446 }
447 
448 static GtkWidget *pbar=NULL;
449 static GtkWidget *pbar_window=NULL;
450 static GtkAdjustment *padj=NULL;
451 
463 void image_update_download_status(int start, int end, int total)
464 {
465  int x, y, wx, wy, w, h;
466 
467  if (start == 1) {
468  padj = (GtkAdjustment*) gtk_adjustment_new (0, 1, total, 0, 0, 0);
469 
470  pbar = gtk_progress_bar_new_with_adjustment(padj);
471  gtk_progress_set_format_string(GTK_PROGRESS(pbar), "Downloading image %v of %u (%p%% complete)");
472  gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(pbar), GTK_PROGRESS_CONTINUOUS);
473  gtk_progress_set_show_text(GTK_PROGRESS(pbar), TRUE);
474  get_window_coord(window_root, &x,&y, &wx,&wy,&w,&h);
475 
476  pbar_window = gtk_window_new(GTK_WINDOW_POPUP);
477  gtk_window_set_policy(GTK_WINDOW(pbar_window), TRUE, TRUE, FALSE);
478  gtk_window_set_transient_for(GTK_WINDOW(pbar_window), GTK_WINDOW (window_root));
479  /*
480  * We more or less want this window centered on the main crossfire
481  * window, and not necessarily centered on the screen or in the upper
482  * left corner.
483  */
484  gtk_widget_set_uposition(pbar_window, (wx + w)/2, (wy + h) / 2);
485 
486  gtk_container_add(GTK_CONTAINER(pbar_window), pbar);
487  gtk_widget_show(pbar);
488  gtk_widget_show(pbar_window);
489  }
490  if (start == total) {
491  gtk_widget_destroy(pbar_window);
492  pbar = NULL;
493  pbar_window = NULL;
494  padj = NULL;
495  return;
496  }
497 
498  gtk_progress_set_value(GTK_PROGRESS(pbar), start);
499  while ( gtk_events_pending() ) {
500  gtk_main_iteration();
501  }
502 }
503 
510 void get_map_image_size(int face, guint8 *w, guint8 *h)
511 {
512  /* We want to calculate the number of spaces this image
513  * uses it. By adding the image size but substracting one,
514  * we cover the cases where the image size is not an even
515  * increment. EG, if the map_image_size is 32, and an image
516  * is 33 wide, we want that to register as two spaces. By
517  * adding 31, that works out.
518  */
519  if ( face < 0 || face >= MAXPIXMAPNUM) {
520  *w = 1;
521  *h = 1;
522  } else {
523  *w = (pixmaps[face]->map_width + map_image_size - 1)/ map_image_size;
524  *h = (pixmaps[face]->map_height + map_image_size - 1)/ map_image_size;
525  }
526 }
527 
528 /******************************************************************************
529  *
530  * Code related to face caching.
531  *
532  *****************************************************************************/
533 
544 {
545  int i;
546  GtkStyle *style;
547 #include "../../pixmaps/question.xpm"
548 
549 
550  LOG(LOG_DEBUG, "gtk-v2::init_image_cache_data", "Init Image Cache");
551 
552  style = gtk_widget_get_style(window_root);
553  pixmaps[0] = g_malloc(sizeof(PixmapInfo));
554  pixmaps[0]->icon_image = gdk_pixmap_create_from_xpm_d(window_root->window,
555  (GdkBitmap**)&pixmaps[0]->icon_mask,
556  &style->bg[GTK_STATE_NORMAL],
557  (gchar **)question_xpm);
558 #ifdef HAVE_SDL
560  /*
561  * Make a semi-transparent question mark symbol to use for the cached
562  * images.
563  */
564 #include "../../pixmaps/question.sdl"
565  pixmaps[0]->map_image = SDL_CreateRGBSurfaceFrom(question_sdl,
566  32, 32, 1, 4, 1, 1, 1, 1);
567  SDL_SetAlpha(pixmaps[0]->map_image, SDL_SRCALPHA, 70);
568  pixmaps[0]->fog_image = SDL_CreateRGBSurfaceFrom(question_sdl,
569  32, 32, 1, 4, 1, 1, 1, 1);
570  SDL_SetAlpha(pixmaps[0]->fog_image, SDL_SRCALPHA, 70);
571  } else
572 #endif
574  pixmaps[0]->map_image = pixmaps[0]->icon_image;
575  pixmaps[0]->fog_image = pixmaps[0]->icon_image;
576  pixmaps[0]->map_mask = pixmaps[0]->icon_mask;
577  }
578 #ifdef HAVE_OPENGL
581  }
582 #endif
583 
584  pixmaps[0]->icon_width = pixmaps[0]->icon_height = pixmaps[0]->map_width = pixmaps[0]->map_height = map_image_size;
585  pixmaps[0]->smooth_face = 0;
586 
587  /* Don't do anything special for SDL image - rather, that drawing
588  * code will check to see if there is no data
589  */
590 
591  /* Initialize all the images to be of the same value. */
592  for (i=1; i<MAXPIXMAPNUM; i++) {
593  pixmaps[i] = pixmaps[0];
594  }
595 
597 }
guint32 width
Definition: image.c:42
void get_window_coord(GtkWidget *win, int *x, int *y, int *wx, int *wy, int *w, int *h)
Gets the coordinates of a specified window.
Definition: main.c:554
#define BPP
Definition: image.c:45
int image_size
Definition: image.c:36
void addsmooth(guint16 face, guint16 smooth_face)
Referenced from common/commands.c.
Definition: image.c:403
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:69
int map_image_size
Definition: map.c:46
void opengl_free_pixmap(PixmapInfo *pi)
#define CONFIG_DISPLAYMODE
Definition: client.h:206
void * fog_image
Definition: image.h:52
guint8 * png_data
Definition: image.c:41
#define MAXPIXMAPNUM
Definition: client.h:535
static GtkWidget * pbar_window
Definition: image.c:449
void * image_data
Definition: client.h:548
void image_update_download_status(int start, int end, int total)
Draws a status bar showing where we our in terms of downloading all the image data.
Definition: image.c:463
void * map_mask
Definition: image.h:50
void create_opengl_map_image(guint8 *data, PixmapInfo *pi)
void * map_image
Definition: image.h:50
guint16 icon_height
Definition: image.h:49
int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, guint8 *rgba_data, int width, int height)
Takes the pixmap to put the data into, as well as the rgba data (ie, already loaded with png_to_data)...
Definition: image.c:261
Contains various global definitions and XML file name and path defaults.
void LOG(LogLevel level, const char *origin, const char *format,...)
Log messages of a certain importance to stderr.
Definition: misc.c:112
#define CFG_DM_PIXMAP
Definition: client.h:251
void init_image_cache_data(void)
Initializes the data for image caching Create question mark to display in each supported rendering mo...
Definition: image.c:543
guint16 smooth_face
A face used for smoothing with this face.
Definition: image.h:53
Pixmap data.
GtkWidget * window_root
In main.c.
Definition: main.c:99
void create_opengl_question_mark(void)
guint32 checksum
Definition: client.h:546
guint16 map_height
Definition: image.h:51
void * icon_image
Definition: image.h:48
guint32 height
Definition: image.c:42
void reset_image_cache_data(void)
We can now connect to different servers, so we need to clear out any old images.
Definition: image.c:509
struct @2 private_cache[MAXPIXMAPNUM]
#define CONFIG_CACHE
Definition: client.h:201
guint16 map_width
Definition: image.h:51
gint16 want_config[CONFIG_NUMS]
Definition: init.c:40
#define MAX_ICON_SPACES
Definition: image.c:68
static void create_map_image(guint8 *data, PixmapInfo *pi)
Helper function to make the code more readable.
Definition: image.c:110
int have_new_image
Definition: image.c:52
cairo_surface_t * rgba_to_cairo_surface(guint8 *data, int width, int height)
Create a Cairo surface for the given RGBA data.
Definition: png.c:518
static void create_icon_image(guint8 *data, PixmapInfo *pi)
Helper function to make the code more readable.
Definition: image.c:99
void reset_image_data(void)
Connecting to different servers, try to clear out any old images.
Definition: image.c:428
#define CONFIG_ICONSCALE
Definition: client.h:203
static GtkAdjustment * padj
Definition: image.c:450
static void free_pixmap(PixmapInfo *pi)
Memory management.
Definition: image.c:206
#define CFG_DM_SDL
Definition: client.h:252
guint16 icon_width
Definition: image.h:49
Used mostly in the cache.c file, however, it can be returned to the graphic side of things so that th...
Definition: client.h:544
PixmapInfo * pixmaps[MAXPIXMAPNUM]
Definition: image.c:47
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)
Takes png data and scales it accordingly.
Definition: png.c:247
static GtkWidget * pbar
Definition: image.c:448
Includes various dependencies header files needed by most everything.
char * name
Definition: image.c:39
int last_face_num
Definition: image.c:49
GdkPixbuf * rgba_to_gdkpixbuf(guint8 *data, int width, int height)
Create a GdkPixbuf for the given RGBA data.
Definition: png.c:492
#define CFG_DM_OPENGL
Definition: client.h:253
Useful debugging information.
Definition: client.h:453
int associate_cache_entry(Cache_Entry *ce, int pixnum)
This functions associates image_data in the cache entry with the specific pixmap number.
Definition: image.c:416
#define CONFIG_MAPSCALE
Definition: client.h:204
void get_map_image_size(int face, guint8 *w, guint8 *h)
Definition: image.c:510