Crossfire Client, Trunk  R19593
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 "config.h"
22 
23 #include <png.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 
27 #ifndef WIN32
28 #include <unistd.h>
29 #endif
30 
31 /* Pick up the gtk headers we need */
32 #include <gtk/gtk.h>
33 #ifndef WIN32
34 #include <gdk/gdkx.h>
35 #else
36 #include <gdk/gdkwin32.h>
37 #endif
38 #include <gdk/gdkkeysyms.h>
39 
40 #ifdef HAVE_SDL
41 #include <SDL.h>
42 #include <SDL_image.h>
43 #endif
44 
45 #include "client-types.h"
46 #include "client.h"
47 #include "image.h"
48 #include "main.h"
49 #include "mapdata.h"
50 #include "gtk2proto.h"
51 
52 extern GtkWidget *window_root;
54 
55 struct {
56  char *name;
57  guint32 checksum;
58  guint8 *png_data;
59  guint32 width, height;
61 
62 #define BPP 4
63 
65 
67 
68 /* Do we have new images to display? */
70 
71 /*
72  * this is used to rescale big images that will be drawn in the inventory/look
73  * lists. What the code further below basically does is figure out how big the
74  * object is (in squares), and this looks at the icon_rescale_factor to figure
75  * what scale factor it gives. Not that the icon_rescale_factor values are
76  * passed directly to the rescale routines. These represent percentages - so
77  * even taking into account that the values diminish as the table grows, they
78  * will still appear larger if the location in the table times the factor is
79  * greater than 100. We find the largest dimension that the image has. The
80  * values in the comment is the effective scaling compared to the base image
81  * size that this big image will appear as. Using a table makes it easier to
82  * adjust the values so things look right.
83  */
84 
85 #define MAX_ICON_SPACES 10
86 static const int icon_rescale_factor[MAX_ICON_SPACES] = {
87  100, 100, 80 /* 2 = 160 */, 60 /* 3 = 180 */,
88  50 /* 4 = 200 */, 45 /* 5 = 225 */, 40 /* 6 = 240 */,
89  35 /* 7 = 259 */, 35 /* 8 = 280 */, 33 /* 9 = 300 */
90 };
91 
92 /******************************************************************************
93  *
94  * Code related to face caching.
95  *
96  *****************************************************************************/
97 
98 /* Does not appear to be used anywhere
99 typedef struct Keys {
100  uint8 flags;
101  sint8 direction;
102  KeySym keysym;
103  char *command;
104  struct Keys *next;
105 } Key_Entry;
106 */
107 
108 /* Rotate right from bsd sum. */
109 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1;
110 
111 /*#define CHECKSUM_DEBUG*/
112 
120 static void create_icon_image(guint8 *data, PixmapInfo *pi, int pixmap_num)
121 {
122  pi->icon_mask = NULL;
123  if (rgba_to_gdkpixbuf(data, pi->icon_width, pi->icon_height,
124  (GdkPixbuf**)&pi->icon_image)) {
125  LOG (LOG_ERROR,"gtk-v2::create_icon_image","Unable to create scaled image, dest num = %d\n", pixmap_num);
126  }
127 }
128 
135 static void create_map_image(guint8 *data, PixmapInfo *pi)
136 {
137  pi->map_image = NULL;
138  pi->map_mask = NULL;
139 
141 #if defined(HAVE_SDL)
142  int i;
143  SDL_Surface *fog;
144  guint32 g,*p;
145  guint8 *l;
146 
147 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
148  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
149  pi->map_height, 32, pi->map_width * 4, 0xff,
150  0xff00, 0xff0000, 0xff000000);
151 
152  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
153  pi->map_width, pi->map_height, 32, 0xff,
154  0xff00, 0xff0000, 0xff000000);
155  SDL_LockSurface(fog);
156 
157  for (i=0; i < pi->map_width * pi->map_height; i++) {
158  l = (guint8 *) (data + i*4);
159 #if 1
160  g = MAX(*l, *(l+1));
161  g = MAX(g, *(l+2));
162 #else
163  g = ( *l + *(l+1) + *(l+2)) / 3;
164 #endif
165  p = (guint32*) fog->pixels + i;
166  *p = g | (g << 8) | (g << 16) | (*(l + 3) << 24);
167  }
168 
169  SDL_UnlockSurface(fog);
170  pi->fog_image = fog;
171 #else
172  /* Big endian */
173  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
174  pi->map_height, 32, pi->map_width * 4, 0xff000000,
175  0xff0000, 0xff00, 0xff);
176 
177  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
178  pi->map_width, pi->map_height, 32, 0xff000000,
179  0xff0000, 0xff00, 0xff);
180  SDL_LockSurface(fog);
181 
182  /*
183  * I think this works out, but haven't tried it on a big endian machine
184  * as my recollection is that the png data would be in the same order,
185  * just the bytes for it to go on the screen are reversed.
186  */
187  for (i=0; i < pi->map_width * pi->map_height; i++) {
188  l = (guint8 *) (data + i*4);
189 #if 1
190  g = MAX(*l, *(l+1));
191  g = MAX(g, *(l+2));
192 #else
193  g = ( *l + *(l+1) + *(l+2)) / 3;
194 #endif
195  p = (guint32*) fog->pixels + i;
196  *p = (g << 8) | (g << 16) | (g << 24) | *(l + 3);
197  }
198 
199  for (i=0; i < pi->map_width * pi->map_height; i+= 4) {
200  guint32 *tmp;
201 
202  /*
203  * The pointer arithemtic below looks suspicious, but it is a patch
204  * that is submitted, so just putting it in as submitted. MSW
205  * 2004-05-11
206  */
207  p = (guint32*) (fog->pixels + i);
208  g = ( ((*p >> 24) & 0xff) + ((*p >> 16) & 0xff) + ((*p >> 8) & 0xff)) / 3;
209  tmp = (guint32*) fog->pixels + i;
210  *tmp = (g << 24) | (g << 16) | (g << 8) | (*p & 0xff);
211  }
212 
213  SDL_UnlockSurface(fog);
214  pi->fog_image = fog;
215 #endif
216 
217 #endif
219 #ifdef HAVE_OPENGL
220  create_opengl_map_image(data, pi);
221 #endif
222  }
223 
225  rgba_to_gdkpixmap(window_root->window, data, pi->map_width, pi->map_height,
226  (GdkPixmap**)&pi->map_image, (GdkBitmap**)&pi->map_mask,
227  gtk_widget_get_colormap(window_root));
228  }
229 }
230 
236 static void free_pixmap(PixmapInfo *pi)
237 {
238  if (pi->icon_image) {
239  g_object_unref(pi->icon_image);
240  }
241  if (pi->icon_mask) {
242  g_object_unref(pi->icon_mask);
243  }
244  if (pi->map_mask) {
245  gdk_pixmap_unref(pi->map_mask);
246  }
248 #ifdef HAVE_SDL
249  if (pi->map_image) {
250  SDL_FreeSurface(pi->map_image);
251  free(((SDL_Surface*)pi->map_image)->pixels);
252  SDL_FreeSurface(pi->fog_image);
253  /*
254  * Minor memory leak here - SDL_FreeSurface() frees the pixel
255  * data _unless_ SDL_CreateRGBSurfaceFrom() was used to create
256  * the surface. SDL_CreateRGBSurfaceFrom() is used to create
257  * the map data, which is why we need the free there. The
258  * reason this is a minor memory leak is because
259  * SDL_CreateRGBSurfaceFrom() is used to create the question
260  * mark image, and without this free, that data is not freed.
261  * However, with this, client crashes after disconnecting from
262  * server with double free.
263  */
264  /* free(((SDL_Surface*)pi->fog_image)->pixels);*/
265  }
266 #endif
268 #ifdef HAVE_OPENGL
269  opengl_free_pixmap(pi);
270 #endif
272  if (pi->map_image) {
273  gdk_pixmap_unref(pi->map_image);
274  }
275  }
276 }
277 
291 int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, guint8 *rgba_data, int width, int height)
292 {
293  int nx, ny, iscale, factor;
294  guint8 *png_tmp;
295  PixmapInfo *pi;
296 
297  if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM) {
298  return 1;
299  }
300 
301  if (pixmaps[pixmap_num] != pixmaps[0]) {
302  /* As per bug 2938906, one can see image corruption when switching between
303  * servers. The cause is that the cache table stores away
304  * a pointer to the pixmap[] entry - if we go and free it,
305  * the cache table can point to garbage, so don't free it.
306  * This causes some memory leak, but if/when there is good
307  * cache support for multiple servers, eventually the amount
308  * of memory consumed will reach a limit (it has every image of
309  * every server in memory
310  *
311  * The cause of image corruption requires a few different things:
312  * 1) images of the same name have different numbers on the 2 serves.
313  * 2) the image number is higher on the first than second server
314  * 3) the image using the high number does not exist/is different
315  * on the second server, causing this routine to be called.
316  */
317 
318  if (!use_config[CONFIG_CACHE]) {
319  free_pixmap(pixmaps[pixmap_num]);
320  free(pixmaps[pixmap_num]);
321  }
322  pixmaps[pixmap_num] = pixmaps[0];
323  }
324 
325  pi = calloc(1, sizeof(PixmapInfo));
326 
327  iscale = use_config[CONFIG_ICONSCALE];
328 
329  /*
330  * If the image is big, figure out what we should scale it to so it fits
331  * better display
332  */
333  if (width > DEFAULT_IMAGE_SIZE || height>DEFAULT_IMAGE_SIZE) {
334  int ts = 100;
335 
336  factor = width / DEFAULT_IMAGE_SIZE;
337  if (factor >= MAX_ICON_SPACES) {
338  factor = MAX_ICON_SPACES - 1;
339  }
340  if (icon_rescale_factor[factor] < ts) {
341  ts = icon_rescale_factor[factor];
342  }
343 
344  factor = height / DEFAULT_IMAGE_SIZE;
345  if (factor >= MAX_ICON_SPACES) {
346  factor = MAX_ICON_SPACES - 1;
347  }
348  if (icon_rescale_factor[factor] < ts) {
349  ts = icon_rescale_factor[factor];
350  }
351 
352  iscale = ts * use_config[CONFIG_ICONSCALE] / 100;
353  }
354 
355  /* In all cases, the icon images are in native form. */
356  if (iscale != 100) {
357  nx=width;
358  ny=height;
359  png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, iscale);
360  pi->icon_width = nx;
361  pi->icon_height = ny;
362  create_icon_image(png_tmp, pi, pixmap_num);
363  free(png_tmp);
364  } else {
365  pi->icon_width = width;
366  pi->icon_height = height;
367  create_icon_image(rgba_data, pi, pixmap_num);
368  }
369 
370  /*
371  * If icon_scale matched use_config[CONFIG_MAPSCALE], we could try to be
372  * more intelligent, but this should not be called too often, and this
373  * keeps the code simpler.
374  */
375  if (use_config[CONFIG_MAPSCALE] != 100) {
376  nx=width;
377  ny=height;
378  png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, use_config[CONFIG_MAPSCALE]);
379  pi->map_width = nx;
380  pi->map_height = ny;
381  create_map_image(png_tmp, pi);
382  /*
383  * pixmap mode and opengl don't need the rgba data after they have
384  * created the image, so we can free it. SDL uses the raw rgba data,
385  * so it can't be freed.
386  */
389  free(png_tmp);
390  }
391  } else {
392  pi->map_width = width;
393  pi->map_height = height;
394  /*
395  * If using SDL mode, a copy of the rgba data needs to be stored away.
396  */
398  png_tmp = g_malloc(width * height * BPP);
399  memcpy(png_tmp, rgba_data, width * height * BPP);
400  } else {
401  png_tmp = rgba_data;
402  }
403  create_map_image(png_tmp, pi);
404  }
405  /*
406  * Not ideal, but if it is missing the map or icon image, presume something
407  * failed. However, opengl doesn't set the map_image, so if using that
408  * display mode, don't make this check.
409  */
411  free_pixmap(pi);
412  free(pi);
413  return 1;
414  }
415  if (ce) {
416  ce->image_data = pi;
417  }
418  pixmaps[pixmap_num] = pi;
419  if (use_config[CONFIG_CACHE]) {
420  have_new_image++;
421  }
422 
423  return 0;
424 }
425 
432 void addsmooth(guint16 face, guint16 smooth_face)
433 {
434  pixmaps[face]->smooth_face = smooth_face;
435 }
436 
445 int associate_cache_entry(Cache_Entry *ce, int pixnum)
446 {
447  pixmaps[pixnum] = ce->image_data;
448  return 0;
449 }
450 
458 {
459  int i;
460 
462  /*
463  * The entries in the pixmaps array are also tracked in the image cache in
464  * the common area. We will try to recycle those images that we can.
465  * Thus, if we connect to a new server, we can just re-use the images we
466  * have already rendered.
467  */
468  for (i=1; i<MAXPIXMAPNUM; i++) {
469  if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) {
470  free_pixmap(pixmaps[i]);
471  free(pixmaps[i]);
472  pixmaps[i] = pixmaps[0];
473  }
474  }
475 }
476 
477 static GtkWidget *pbar=NULL;
478 static GtkWidget *pbar_window=NULL;
479 static GtkAdjustment *padj=NULL;
480 
492 void image_update_download_status(int start, int end, int total)
493 {
494  int x, y, wx, wy, w, h;
495 
496  if (start == 1) {
497  padj = (GtkAdjustment*) gtk_adjustment_new (0, 1, total, 0, 0, 0);
498 
499  pbar = gtk_progress_bar_new_with_adjustment(padj);
500  gtk_progress_set_format_string(GTK_PROGRESS(pbar), "Downloading image %v of %u (%p%% complete)");
501  gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(pbar), GTK_PROGRESS_CONTINUOUS);
502  gtk_progress_set_show_text(GTK_PROGRESS(pbar), TRUE);
503  get_window_coord(window_root, &x,&y, &wx,&wy,&w,&h);
504 
505  pbar_window = gtk_window_new(GTK_WINDOW_POPUP);
506  gtk_window_set_policy(GTK_WINDOW(pbar_window), TRUE, TRUE, FALSE);
507  gtk_window_set_transient_for(GTK_WINDOW(pbar_window), GTK_WINDOW (window_root));
508  /*
509  * We more or less want this window centered on the main crossfire
510  * window, and not necessarily centered on the screen or in the upper
511  * left corner.
512  */
513  gtk_widget_set_uposition(pbar_window, (wx + w)/2, (wy + h) / 2);
514 
515  gtk_container_add(GTK_CONTAINER(pbar_window), pbar);
516  gtk_widget_show(pbar);
517  gtk_widget_show(pbar_window);
518  }
519  if (start == total) {
520  gtk_widget_destroy(pbar_window);
521  pbar = NULL;
522  pbar_window = NULL;
523  padj = NULL;
524  return;
525  }
526 
527  gtk_progress_set_value(GTK_PROGRESS(pbar), start);
528  while ( gtk_events_pending() ) {
529  gtk_main_iteration();
530  }
531 }
532 
539 void get_map_image_size(int face, guint8 *w, guint8 *h)
540 {
541  /* We want to calculate the number of spaces this image
542  * uses it. By adding the image size but substracting one,
543  * we cover the cases where the image size is not an even
544  * increment. EG, if the map_image_size is 32, and an image
545  * is 33 wide, we want that to register as two spaces. By
546  * adding 31, that works out.
547  */
548  if ( face < 0 || face >= MAXPIXMAPNUM) {
549  *w = 1;
550  *h = 1;
551  } else {
552  *w = (pixmaps[face]->map_width + map_image_size - 1)/ map_image_size;
553  *h = (pixmaps[face]->map_height + map_image_size - 1)/ map_image_size;
554  }
555 }
556 
557 /******************************************************************************
558  *
559  * Code related to face caching.
560  *
561  *****************************************************************************/
562 
573 {
574  int i;
575  GtkStyle *style;
576 #include "../../pixmaps/question.xpm"
577 
578 
579  LOG(LOG_INFO,"gtk-v2::init_image_cache_data","Init Image Cache");
580 
581  style = gtk_widget_get_style(window_root);
582  pixmaps[0] = g_malloc(sizeof(PixmapInfo));
583  pixmaps[0]->icon_image = gdk_pixmap_create_from_xpm_d(window_root->window,
584  (GdkBitmap**)&pixmaps[0]->icon_mask,
585  &style->bg[GTK_STATE_NORMAL],
586  (gchar **)question_xpm);
587 #ifdef HAVE_SDL
589  /*
590  * Make a semi-transparent question mark symbol to use for the cached
591  * images.
592  */
593 #include "../../pixmaps/question.sdl"
594  pixmaps[0]->map_image = SDL_CreateRGBSurfaceFrom(question_sdl,
595  32, 32, 1, 4, 1, 1, 1, 1);
596  SDL_SetAlpha(pixmaps[0]->map_image, SDL_SRCALPHA, 70);
597  pixmaps[0]->fog_image = SDL_CreateRGBSurfaceFrom(question_sdl,
598  32, 32, 1, 4, 1, 1, 1, 1);
599  SDL_SetAlpha(pixmaps[0]->fog_image, SDL_SRCALPHA, 70);
600  } else
601 #endif
603  pixmaps[0]->map_image = pixmaps[0]->icon_image;
604  pixmaps[0]->fog_image = pixmaps[0]->icon_image;
605  pixmaps[0]->map_mask = pixmaps[0]->icon_mask;
606  }
607 #ifdef HAVE_OPENGL
610  }
611 #endif
612 
613  pixmaps[0]->icon_width = pixmaps[0]->icon_height = pixmaps[0]->map_width = pixmaps[0]->map_height = map_image_size;
614  pixmaps[0]->smooth_face = 0;
615 
616  /* Don't do anything special for SDL image - rather, that drawing
617  * code will check to see if there is no data
618  */
619 
620  /* Initialize all the images to be of the same value. */
621  for (i=1; i<MAXPIXMAPNUM; i++) {
622  pixmaps[i] = pixmaps[0];
623  }
624 
626 }
guint32 width
Definition: image.c:59
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:604
#define BPP
Definition: image.c:62
int rgba_to_gdkpixmap(GdkWindow *window, guint8 *data, int width, int height, GdkPixmap **pix, GdkBitmap **mask, GdkColormap *colormap)
Takes data that has already been converted into RGBA format (via png_to_data above perhaps) and creat...
Definition: png.c:455
int image_size
Definition: image.c:53
void addsmooth(guint16 face, guint16 smooth_face)
Referenced from common/commands.c.
Definition: image.c:432
gint16 use_config[CONFIG_NUMS]
Definition: init.c:39
#define DEFAULT_IMAGE_SIZE
Definition: image.h:40
static const int icon_rescale_factor[MAX_ICON_SPACES]
Definition: image.c:86
int map_image_size
Definition: map.c:57
void opengl_free_pixmap(PixmapInfo *pi)
#define CONFIG_DISPLAYMODE
Definition: client.h:167
void * fog_image
Definition: image.h:52
guint8 * png_data
Definition: image.c:58
#define MAXPIXMAPNUM
Definition: client.h:493
static GtkWidget * pbar_window
Definition: image.c:478
Client type definitions.
void * image_data
Definition: client.h:506
static void create_icon_image(guint8 *data, PixmapInfo *pi, int pixmap_num)
Helper function to make the code more readable.
Definition: image.c:120
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:492
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:291
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:111
#define CFG_DM_PIXMAP
Definition: client.h:212
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:572
int rgba_to_gdkpixbuf(guint8 *data, int width, int height, GdkPixbuf **pix)
Takes data that has already been converted into RGBA format (via png_to_data above perhaps) and creat...
Definition: png.c:514
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:103
void create_opengl_question_mark(void)
guint32 checksum
Definition: client.h:504
guint16 map_height
Definition: image.h:51
void * icon_image
Definition: image.h:48
guint32 height
Definition: image.c:59
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:514
struct @2 private_cache[MAXPIXMAPNUM]
#define CONFIG_CACHE
Definition: client.h:162
guint16 map_width
Definition: image.h:51
gint16 want_config[CONFIG_NUMS]
Definition: init.c:39
#define MAX_ICON_SPACES
Definition: image.c:85
static void create_map_image(guint8 *data, PixmapInfo *pi)
Helper function to make the code more readable.
Definition: image.c:135
int have_new_image
Definition: image.c:69
void reset_image_data(void)
Connecting to different servers, try to clear out any old images.
Definition: image.c:457
#define CONFIG_ICONSCALE
Definition: client.h:164
static GtkAdjustment * padj
Definition: image.c:479
static void free_pixmap(PixmapInfo *pi)
Memory management.
Definition: image.c:236
#define CFG_DM_SDL
Definition: client.h:213
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:502
PixmapInfo * pixmaps[MAXPIXMAPNUM]
Definition: image.c:64
void * icon_mask
Definition: image.h:48
void init_common_cache_data(void)
Definition: image.c:364
guint8 * rescale_rgba_data(guint8 *data, int *width, int *height, int scale)
Takes png data and scales it accordingly.
Definition: png.c:262
static GtkWidget * pbar
Definition: image.c:477
Includes various dependencies header files needed by most everything.
char * name
Definition: image.c:56
int last_face_num
Definition: image.c:66
#define CFG_DM_OPENGL
Definition: client.h:214
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:445
#define CONFIG_MAPSCALE
Definition: client.h:165
void get_map_image_size(int face, guint8 *w, guint8 *h)
Definition: image.c:539