Crossfire Client, Branches  R11627
image.c
Go to the documentation of this file.
1 const char * const rcsid_gtk2_image_c =
2  "$Id: image.c 11043 2008-12-21 04:44:14Z kbulgrien $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2005 Mark Wedel & Crossfire Development Team
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  The author can be reached via e-mail to crossfire@metalforge.org
23 */
24 
32 #include <config.h>
33 #include <stdlib.h>
34 #include <sys/stat.h>
35 #ifndef WIN32
36 #include <unistd.h>
37 #endif
38 #include <png.h>
39 
40 /* Pick up the gtk headers we need */
41 #include <gtk/gtk.h>
42 #include <glade/glade.h>
43 #ifndef WIN32
44 #include <gdk/gdkx.h>
45 #else
46 #include <gdk/gdkwin32.h>
47 #endif
48 #include <gdk/gdkkeysyms.h>
49 
50 #ifdef HAVE_SDL
51 #include <SDL.h>
52 #include <SDL_image.h>
53 #endif
54 
55 #include "client-types.h"
56 #include "client.h"
57 #include "image.h"
58 #include "main.h"
59 #include "mapdata.h"
60 #include "gtk2proto.h"
61 
62 extern GtkWidget *window_root;
64 
65 struct {
66  char *name;
71 
72 #define BPP 4
73 
75 
77 
78 /*
79  * this is used to rescale big images that will be drawn in the inventory/look
80  * lists. What the code further below basically does is figure out how big the
81  * object is (in squares), and this looks at the icon_rescale_factor to figure
82  * what scale factor it gives. Not that the icon_rescale_factor values are
83  * passed directly to the rescale routines. These represent percentages - so
84  * even taking into account that the values diminish as the table grows, they
85  * will still appear larger if the location in the table times the factor is
86  * greater than 100. We find the largest dimension that the image has. The
87  * values in the comment is the effective scaling compared to the base image
88  * size that this big image will appear as. Using a table makes it easier to
89  * adjust the values so things look right.
90  */
91 
92 #define MAX_ICON_SPACES 10
93 static const int icon_rescale_factor[MAX_ICON_SPACES] = {
94 100, 100, 80 /* 2 = 160 */, 60 /* 3 = 180 */,
95 50 /* 4 = 200 */, 45 /* 5 = 225 */, 40 /* 6 = 240 */,
96 35 /* 7 = 259 */, 35 /* 8 = 280 */, 33 /* 9 = 300 */
97 };
98 
99 /******************************************************************************
100  *
101  * Code related to face caching.
102  *
103  *****************************************************************************/
104 
105 /* Does not appear to be used anywhere
106 typedef struct Keys {
107  uint8 flags;
108  sint8 direction;
109  KeySym keysym;
110  char *command;
111  struct Keys *next;
112 } Key_Entry;
113 */
114 
115 /* Rotate right from bsd sum. */
116 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1;
117 
118 /*#define CHECKSUM_DEBUG*/
119 
127 static void create_icon_image(uint8 *data, PixmapInfo *pi, int pixmap_num)
128 {
129  pi->icon_mask = NULL;
130  if (rgba_to_gdkpixbuf(data, pi->icon_width, pi->icon_height,
131  (GdkPixbuf**)&pi->icon_image))
132  LOG (LOG_ERROR,"gtk::create_icon_image","Unable to create scaled image, dest num = %d\n", pixmap_num);
133 }
134 
141 static void create_map_image(uint8 *data, PixmapInfo *pi)
142 {
143  pi->map_image = NULL;
144  pi->map_mask = NULL;
145 
147 #if defined(HAVE_SDL)
148  int i;
149  SDL_Surface *fog;
150  uint32 g,*p;
151  uint8 *l;
152 
153 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
154  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
155  pi->map_height, 32, pi->map_width * 4, 0xff,
156  0xff00, 0xff0000, 0xff000000);
157 
158  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
159  pi->map_width, pi->map_height, 32, 0xff,
160  0xff00, 0xff0000, 0xff000000);
161  SDL_LockSurface(fog);
162 
163  for (i=0; i < pi->map_width * pi->map_height; i++) {
164  l = (uint8 *) (data + i*4);
165 #if 1
166  g = MAX(*l, *(l+1));
167  g = MAX(g, *(l+2));
168 #else
169  g = ( *l + *(l+1) + *(l+2)) / 3;
170 #endif
171  p = (uint32*) fog->pixels + i;
172  *p = g | (g << 8) | (g << 16) | (*(l + 3) << 24);
173  }
174 
175  SDL_UnlockSurface(fog);
176  pi->fog_image = fog;
177  #else
178  /* Big endian */
179  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
180  pi->map_height, 32, pi->map_width * 4, 0xff000000,
181  0xff0000, 0xff00, 0xff);
182 
183  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
184  pi->map_width, pi->map_height, 32, 0xff000000,
185  0xff0000, 0xff00, 0xff);
186  SDL_LockSurface(fog);
187 
188  /*
189  * I think this works out, but haven't tried it on a big endian machine
190  * as my recollection is that the png data would be in the same order,
191  * just the bytes for it to go on the screen are reversed.
192  */
193  for (i=0; i < pi->map_width * pi->map_height; i++) {
194  l = (uint8 *) (data + i*4);
195 #if 1
196  g = MAX(*l, *(l+1));
197  g = MAX(g, *(l+2));
198 #else
199  g = ( *l + *(l+1) + *(l+2)) / 3;
200 #endif
201  p = (uint32*) fog->pixels + i;
202  *p = (g << 8) | (g << 16) | (g << 24) | *(l + 3);
203  }
204 
205  for (i=0; i < pi->map_width * pi->map_height; i+= 4) {
206  uint32 *tmp;
207 
208  /*
209  * The pointer arithemtic below looks suspicious, but it is a patch
210  * that is submitted, so just putting it in as submitted. MSW
211  * 2004-05-11
212  */
213  p = (uint32*) (fog->pixels + i);
214  g = ( ((*p >> 24) & 0xff) + ((*p >> 16) & 0xff) + ((*p >> 8) & 0xff)) / 3;
215  tmp = (uint32*) fog->pixels + i;
216  *tmp = (g << 24) | (g << 16) | (g << 8) | (*p & 0xff);
217  }
218 
219  SDL_UnlockSurface(fog);
220  pi->fog_image = fog;
221  #endif
222 
223 #endif
224  }
226 #ifdef HAVE_OPENGL
227  create_opengl_map_image(data, pi);
228 #endif
229  }
230 
232  rgba_to_gdkpixmap(window_root->window, data, pi->map_width, pi->map_height,
233  (GdkPixmap**)&pi->map_image, (GdkBitmap**)&pi->map_mask,
234  gtk_widget_get_colormap(window_root));
235  }
236 }
237 
243 static void free_pixmap(PixmapInfo *pi)
244 {
245  if (pi->icon_image) g_object_unref(pi->icon_image);
246  if (pi->icon_mask) g_object_unref(pi->icon_mask);
247  if (pi->map_mask) gdk_pixmap_unref(pi->map_mask);
249 #ifdef HAVE_SDL
250  if (pi->map_image) {
251  SDL_FreeSurface(pi->map_image);
252  free(((SDL_Surface*)pi->map_image)->pixels);
253  SDL_FreeSurface(pi->fog_image);
254  /*
255  * Minor memory leak here - SDL_FreeSurface() frees the pixel
256  * data _unless_ SDL_CreateRGBSurfaceFrom() was used to create
257  * the surface. SDL_CreateRGBSurfaceFrom() is used to create
258  * the map data, which is why we need the free there. The
259  * reason this is a minor memory leak is because
260  * SDL_CreateRGBSurfaceFrom() is used to create the question
261  * mark image, and without this free, that data is not freed.
262  * However, with this, client crashes after disconnecting from
263  * server with double free.
264  */
265  /* free(((SDL_Surface*)pi->fog_image)->pixels);*/
266  }
267 #endif
268  }
270 #ifdef HAVE_OPENGL
271  opengl_free_pixmap(pi);
272 #endif
273  }
275  if (pi->map_image) {
276  gdk_pixmap_unref(pi->map_image);
277  }
278  }
279 }
280 
294 int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height)
295 {
296  int nx, ny, iscale, factor;
297  uint8 *png_tmp;
298  PixmapInfo *pi;
299 
300  if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM)
301  return 1;
302 
303  if (pixmaps[pixmap_num] != pixmaps[0]) {
304  free_pixmap(pixmaps[pixmap_num]);
305  free(pixmaps[pixmap_num]);
306  pixmaps[pixmap_num] = pixmaps[0];
307  }
308 
309  pi = calloc(1, sizeof(PixmapInfo));
310 
311  iscale = use_config[CONFIG_ICONSCALE];
312 
313  /*
314  * If the image is big, figure out what we should scale it to so it fits
315  * better display
316  */
317  if (width > DEFAULT_IMAGE_SIZE || height>DEFAULT_IMAGE_SIZE) {
318  int ts = 100;
319 
320  factor = width / DEFAULT_IMAGE_SIZE;
321  if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1;
322  if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor];
323 
324  factor = height / DEFAULT_IMAGE_SIZE;
325  if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1;
326  if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor];
327 
328  iscale = ts * use_config[CONFIG_ICONSCALE] / 100;
329  }
330 
331  /* In all cases, the icon images are in native form. */
332  if (iscale != 100) {
333  nx=width;
334  ny=height;
335  png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, iscale);
336  pi->icon_width = nx;
337  pi->icon_height = ny;
338  create_icon_image(png_tmp, pi, pixmap_num);
339  free(png_tmp);
340  }
341  else {
342  pi->icon_width = width;
343  pi->icon_height = height;
344  create_icon_image(rgba_data, pi, pixmap_num);
345  }
346 
347  /*
348  * If icon_scale matched use_config[CONFIG_MAPSCALE], we could try to be
349  * more intelligent, but this should not be called too often, and this
350  * keeps the code simpler.
351  */
352  if (use_config[CONFIG_MAPSCALE] != 100) {
353  nx=width;
354  ny=height;
355  png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, use_config[CONFIG_MAPSCALE]);
356  pi->map_width = nx;
357  pi->map_height = ny;
358  create_map_image(png_tmp, pi);
359  /*
360  * pixmap mode and opengl don't need the rgba data after they have
361  * created the image, so we can free it. SDL uses the raw rgba data,
362  * so it can't be freed.
363  */
366  } else {
367  pi->map_width = width;
368  pi->map_height = height;
369  /*
370  * If using SDL mode, a copy of the rgba data needs to be stored away.
371  */
373  png_tmp = malloc(width * height * BPP);
374  memcpy(png_tmp, rgba_data, width * height * BPP);
375  } else
376  png_tmp = rgba_data;
377  create_map_image(png_tmp, pi);
378  }
379  /*
380  * Not ideal, but if it is missing the map or icon image, presume something
381  * failed. However, opengl doesn't set the map_image, so if using that
382  * display mode, don't make this check.
383  */
385  free_pixmap(pi);
386  free(pi);
387  return 1;
388  }
389  if (ce) {
390  ce->image_data = pi;
391  }
392  pixmaps[pixmap_num] = pi;
393  return 0;
394 }
395 
402 void addsmooth(uint16 face, uint16 smooth_face)
403 {
404  pixmaps[face]->smooth_face = smooth_face;
405 }
406 
415 int associate_cache_entry(Cache_Entry *ce, int pixnum)
416 {
417  pixmaps[pixnum] = ce->image_data;
418  return 0;
419 }
420 
428 {
429  int i;
430 
432  /*
433  * The entries in the pixmaps array are also tracked in the image cache in
434  * the common area. We will try to recycle those images that we can.
435  * Thus, if we connect to a new server, we can just re-use the images we
436  * have already rendered.
437  */
438  for (i=1; i<MAXPIXMAPNUM; i++) {
439  if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) {
440  free_pixmap(pixmaps[i]);
441  free(pixmaps[i]);
442  pixmaps[i] = pixmaps[0];
443  }
444  }
445 }
446 
447 static GtkWidget *pbar=NULL;
448 static GtkWidget *pbar_window=NULL;
449 static GtkAdjustment *padj=NULL;
450 
462 void image_update_download_status(int start, int end, int total)
463 {
464  int x, y, wx, wy, w, h;
465 
466  if (start == 1) {
467  padj = (GtkAdjustment*) gtk_adjustment_new (0, 1, total, 0, 0, 0);
468 
469  pbar = gtk_progress_bar_new_with_adjustment(padj);
470  gtk_progress_set_format_string(GTK_PROGRESS(pbar), "Downloading image %v of %u (%p%% complete)");
471  gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(pbar), GTK_PROGRESS_CONTINUOUS);
472  gtk_progress_set_show_text(GTK_PROGRESS(pbar), TRUE);
473  get_window_coord(window_root, &x,&y, &wx,&wy,&w,&h);
474 
475  pbar_window = gtk_window_new(GTK_WINDOW_POPUP);
476  gtk_window_set_policy(GTK_WINDOW(pbar_window), TRUE, TRUE, FALSE);
477  gtk_window_set_transient_for(GTK_WINDOW(pbar_window), GTK_WINDOW (window_root));
478  /*
479  * We more or less want this window centered on the main crossfire
480  * window, and not necessarily centered on the screen or in the upper
481  * left corner.
482  */
483  gtk_widget_set_uposition(pbar_window, (wx + w)/2, (wy + h) / 2);
484 
485  gtk_container_add(GTK_CONTAINER(pbar_window), pbar);
486  gtk_widget_show(pbar);
487  gtk_widget_show(pbar_window);
488  }
489  if (start == total) {
490  gtk_widget_destroy(pbar_window);
491  pbar = NULL;
492  pbar_window = NULL;
493  padj = NULL;
494  return;
495  }
496 
497  gtk_progress_set_value(GTK_PROGRESS(pbar), start);
498  while ( gtk_events_pending() ) {
499  gtk_main_iteration();
500  }
501 }
502 
509 void get_map_image_size(int face, uint8 *w, uint8 *h)
510 {
511  /* We want to calculate the number of spaces this image
512  * uses it. By adding the image size but substracting one,
513  * we cover the cases where the image size is not an even
514  * increment. EG, if the map_image_size is 32, and an image
515  * is 33 wide, we want that to register as two spaces. By
516  * adding 31, that works out.
517  */
518  if ( face < 0 || face >= MAXPIXMAPNUM) {
519  *w = 1;
520  *h = 1;
521  } else {
522  *w = (pixmaps[face]->map_width + map_image_size - 1)/ map_image_size;
523  *h = (pixmaps[face]->map_height + map_image_size - 1)/ map_image_size;
524  }
525 }
526 
527 /******************************************************************************
528  *
529  * Code related to face caching.
530  *
531  *****************************************************************************/
532 
542 void init_cache_data(void)
543 {
544  int i;
545  GtkStyle *style;
546 #include "../../pixmaps/question.xpm"
547 
548 
549  LOG(LOG_INFO,"gtk::init_cache_data","Init Cache");
550 
551  style = gtk_widget_get_style(window_root);
552  pixmaps[0] = malloc(sizeof(PixmapInfo));
553  pixmaps[0]->icon_image = gdk_pixmap_create_from_xpm_d(window_root->window,
554  (GdkBitmap**)&pixmaps[0]->icon_mask,
555  &style->bg[GTK_STATE_NORMAL],
556  (gchar **)question_xpm);
557 #ifdef HAVE_SDL
559  /*
560  * Make a semi-transparent question mark symbol to use for the cached
561  * images.
562  */
563 #include "../../pixmaps/question.sdl"
564  pixmaps[0]->map_image = SDL_CreateRGBSurfaceFrom(question_sdl,
565  32, 32, 1, 4, 1, 1, 1, 1);
566  SDL_SetAlpha(pixmaps[0]->map_image, SDL_SRCALPHA, 70);
567  pixmaps[0]->fog_image = SDL_CreateRGBSurfaceFrom(question_sdl,
568  32, 32, 1, 4, 1, 1, 1, 1);
569  SDL_SetAlpha(pixmaps[0]->fog_image, SDL_SRCALPHA, 70);
570  }
571  else
572 #endif
574  {
575  pixmaps[0]->map_image = pixmaps[0]->icon_image;
576  pixmaps[0]->fog_image = pixmaps[0]->icon_image;
577  pixmaps[0]->map_mask = pixmaps[0]->icon_mask;
578  }
579 #ifdef HAVE_OPENGL
582  }
583 #endif
584 
585  pixmaps[0]->icon_width = pixmaps[0]->icon_height = pixmaps[0]->map_width = pixmaps[0]->map_height = map_image_size;
586  pixmaps[0]->smooth_face = 0;
587 
588  /* Don't do anything special for SDL image - rather, that drawing
589  * code will check to see if there is no data
590  */
591 
592  /* Initialize all the images to be of the same value. */
593  for (i=1; i<MAXPIXMAPNUM; i++) {
594  pixmaps[i] = pixmaps[0];
595  }
596 
598 }
#define BPP
Definition: image.c:72
int image_size
Definition: image.c:63
#define DEFAULT_IMAGE_SIZE
Definition: gx11.c:114
int last_face_num
Definition: image.c:69
static const int icon_rescale_factor[MAX_ICON_SPACES]
Definition: image.c:93
void opengl_free_pixmap(PixmapInfo *pi)
uint16 icon_height
Definition: gx11.h:62
void image_update_download_status(int start, int end, int total)
Definition: image.c:419
struct @2 private_cache[MAXPIXMAPNUM]
int total
Definition: gx11.c:322
void get_window_coord(GtkWidget *win, int *x, int *y, int *wx, int *wy, int *w, int *h)
Definition: gx11.c:4789
void * fog_image
Definition: gx11.h:65
#define MAXPIXMAPNUM
Definition: client.h:470
sint16 want_config[CONFIG_NUMS]
Definition: init.c:50
#define CONFIG_MAPSCALE
Definition: client.h:159
uint32 width
Definition: image.c:64
void create_opengl_map_image(uint8 *data, PixmapInfo *pi)
static GtkWidget * pbar_window
Definition: image.c:448
void * image_data
Definition: client.h:484
int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height)
Definition: image.c:252
uint16 map_height
Definition: gx11.h:64
uint16 icon_width
Definition: gx11.h:62
void * map_image
Definition: gx11.h:63
#define CFG_DM_SDL
Definition: client.h:195
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
GtkWidget * window_root
Definition: main.c:56
void addsmooth(uint16 face, uint16 smooth_face)
Definition: image.c:360
uint8 * png_data
Definition: image.c:63
void init_cache_data(void)
Definition: image.c:542
void create_opengl_question_mark(void)
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
void * map_mask
Definition: gx11.h:63
void * icon_image
Definition: gx11.h:61
static void create_map_image(uint8 *data, PixmapInfo *pi)
Definition: image.c:141
#define CONFIG_CACHE
Definition: client.h:156
char * name
Definition: image.c:61
static void create_icon_image(uint8 *data, PixmapInfo *pi, int pixmap_num)
Definition: image.c:127
void reset_image_cache_data(void)
Definition: image.c:474
uint8 * rescale_rgba_data(uint8 *data, int *width, int *height, int scale)
Definition: png.c:244
unsigned short uint16
Definition: client-types.h:79
void reset_image_data(void)
Definition: image.c:387
int rgba_to_gdkpixmap(GdkWindow *window, uint8 *data, int width, int height, GdkPixmap **pix, GdkBitmap **mask, GdkColormap *colormap)
Definition: png.c:407
#define MAX_ICON_SPACES
Definition: image.c:92
uint32 height
Definition: image.c:64
#define CFG_DM_PIXMAP
Definition: client.h:194
unsigned int uint32
Definition: client-types.h:77
#define CONFIG_ICONSCALE
Definition: client.h:158
int rgba_to_gdkpixbuf(uint8 *data, int width, int height, GdkPixbuf **pix)
Definition: png.c:500
static GtkAdjustment * padj
Definition: image.c:449
static void free_pixmap(PixmapInfo *pi)
Definition: image.c:243
Definition: client.h:480
PixmapInfo * pixmaps[MAXPIXMAPNUM]
Definition: image.c:74
void get_map_image_size(int face, uint8 *w, uint8 *h)
Definition: image.c:459
void init_common_cache_data(void)
Definition: image.c:332
uint16 smooth_face
Definition: gx11.h:66
static GtkWidget * pbar
Definition: image.c:447
#define CFG_DM_OPENGL
Definition: client.h:196
#define CONFIG_DISPLAYMODE
Definition: client.h:161
unsigned char uint8
Definition: client-types.h:81
int associate_cache_entry(Cache_Entry *ce, int pixnum)
Definition: image.c:372
#define FALSE
Definition: client-types.h:68
const char *const rcsid_gtk2_image_c
Definition: image.c:1
uint32 checksum
Definition: image.c:62
uint16 map_width
Definition: gx11.h:64
void * icon_mask
Definition: gx11.h:61