Crossfire Client, Branches  R11627
image.c
Go to the documentation of this file.
1 const char * const rcsid_gtk_image_c =
2  "$Id: image.c 9201 2008-06-01 17:32:45Z anmaster $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2001 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-devel@real-time.com
23 */
24 
25 /*
26  * This file contains image related functions - this is a higher level up -
27  * it mostly deals with the caching of the images, processing the image commands
28  * from the server, etc. This file is gtk specific - at least it returns
29  * gtk pixmaps.
30  */
31 
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 #ifndef WIN32
43 #include <gdk/gdkx.h>
44 #else
45 #include <gdk/gdkwin32.h>
46 #endif
47 #include <gdk/gdkkeysyms.h>
48 
49 #ifdef HAVE_SDL
50 #include <SDL.h>
51 #include <SDL_image.h>
52 #endif
53 
54 #include "client-types.h"
55 #include "gx11.h"
56 #include "client.h"
57 
58 #include "gtkproto.h"
59 
60 struct {
61  char *name;
66 
67 #define BPP 4
68 
70 
71 /* this is used to rescale big images that will be drawn in the inventory/look
72  * lists. What the code further below basically does is figure out how big
73  * the object is (in squares), and this looks at the icon_rescale_factor
74  * to figure what scale factor it gives. Not that the icon_rescale_factor
75  * values are passed directly to the rescale routines. These represent
76  * percentages - so even taking into account that the values diminish
77  * as the table grows, they will still appear larger if the location
78  * in the table times the factor is greater than 100. We find
79  * the largest dimension that the image has. The values in the comment
80  * is the effective scaling compared to the base image size that this
81  * big image will appear as.
82  * Using a table makes it easier to adjust the values so things
83  * look right.
84  */
85 
86 #define MAX_ICON_SPACES 10
88 100, 100, 80 /* 2 = 160 */, 60 /* 3 = 180 */,
89 50 /* 4 = 200 */, 45 /* 5 = 225 */, 40 /* 6 = 240 */,
90 35 /* 7 = 259 */, 35 /* 8 = 280 */, 33 /* 9 = 300 */
91 };
92 
93 /******************************************************************************
94  *
95  * Code related to face caching.
96  *
97  *****************************************************************************/
98 
99 typedef struct Keys {
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 
113 /* These little helper functions just make the code below much more readable */
114 static void create_icon_image(uint8 *data, PixmapInfo *pi, int pixmap_num)
115 {
116  if (rgba_to_gdkpixmap(gtkwin_root->window, data, pi->icon_width, pi->icon_height,
117  (GdkPixmap**)&pi->icon_image, (GdkBitmap**)&pi->icon_mask,
118  gtk_widget_get_colormap(gtkwin_root)))
119  LOG (LOG_ERROR,"gtk::create_icon_image","Unable to create scaled image, dest num = %d\n", pixmap_num);
120 }
121 
122 /* These little helper functions just make the code below much more readable */
123 static void create_map_image(uint8 *data, PixmapInfo *pi)
124 {
125  pi->map_image = NULL;
126  pi->map_mask = NULL;
127 
129 #if defined(HAVE_SDL)
130  int i;
131  SDL_Surface *fog;
132  uint32 g,*p;
133  uint8 *l;
134 
135  #if SDL_BYTEORDER == SDL_LIL_ENDIAN
136  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
137  pi->map_height, 32, pi->map_width * 4, 0xff,
138  0xff00, 0xff0000, 0xff000000);
139 
140  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
141  pi->map_width, pi->map_height, 32, 0xff,
142  0xff00, 0xff0000, 0xff000000);
143  SDL_LockSurface(fog);
144 
145  for (i=0; i < pi->map_width * pi->map_height; i++) {
146  l = (uint8 *) (data + i*4);
147  /* The effective luminance of a pixel is calculated with the following formula:
148  * Y=0.3RED+0.59GREEN+0.11Blue
149  * This is the way you are supposed to convert color to grayscale
150  * for best 'natural' results. Using maxes, mins or simple mean is
151  * gives awfull results with darkness code.
152  * For speed reason i work here in integers
153  * (77RED+151GREEN+28BLUE)>>8
154  *
155  * Tchize 22 May 2004
156  */
157  g=(77*(*l)+151*(*(l+1))+28*(*(l+2)))>>8;
158  p = (uint32*) fog->pixels + i;
159  *p = g | (g << 8) | (g << 16) | (*(l + 3) << 24);
160  }
161 
162  SDL_UnlockSurface(fog);
163  pi->fog_image = fog;
164  #else
165  /* Big endian */
166  pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width,
167  pi->map_height, 32, pi->map_width * 4, 0xff000000,
168  0xff0000, 0xff00, 0xff);
169 
170  fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE,
171  pi->map_width, pi->map_height, 32, 0xff000000,
172  0xff0000, 0xff00, 0xff);
173  SDL_LockSurface(fog);
174 
175  /* I think this works out, but haven't tried it on a big
176  * endian machine - my recollection is that the png data
177  * would be in the same order, just the bytes for it to go
178  * on teh screen are reversed.
179  */
180  for (i=0; i < pi->map_width * pi->map_height; i++) {
181  l = (uint8 *) (data + i*4);
182  /* The effective luminance of a pixel is calculated with the following formula:
183  * Y=0.3RED+0.59GREEN+0.11Blue
184  * This is the way you are supposed to convert color to grayscale
185  * for best 'natural' results. Using maxes, mins or simple mean is
186  * gives awfull results with darkness code.
187  * For speed reason i work here in integers
188  * (77RED+151GREEN+28BLUE)>>8
189  *
190  * Tchize 22 May 2004
191  */
192  g=(77*(*l)+151*(*(l+1))+28*(*(l+2)))>>8;
193  p = (uint32*) fog->pixels + i;
194  *p = (g << 8) | (g << 16) | (g << 24) | *(l + 3);
195  }
196 
197  for (i=0; i < pi->map_width * pi->map_height; i+= 4) {
198  uint32 *tmp;
199 
200  /* The pointer arithemtic below looks suspicious, but it is a patch that
201  * is submitted, so just putting it in as submitted.
202  * MSW 2004-05-11
203  */
204  p = (uint32*) (fog->pixels + i);
205  g = ( ((*p >> 24) & 0xff) + ((*p >> 16) & 0xff) + ((*p >> 8) & 0xff)) / 3;
206  tmp = (uint32*) fog->pixels + i;
207  *tmp = (g << 24) | (g << 16) | (g << 8) | (*p & 0xff);
208 
209  }
210 
211  SDL_UnlockSurface(fog);
212  pi->fog_image = fog;
213  #endif
214 
215 #endif
216  }
217  else {
218  rgba_to_gdkpixmap(gtkwin_root->window, data, pi->map_width, pi->map_height,
219  (GdkPixmap**)&pi->map_image, (GdkBitmap**)&pi->map_mask,
220  gtk_widget_get_colormap(gtkwin_root));
221  }
222 }
223 
224 static void free_pixmap(PixmapInfo *pi)
225 {
226  if (pi->icon_image) gdk_pixmap_unref(pi->icon_image);
227  if (pi->icon_mask) gdk_pixmap_unref(pi->icon_mask);
228  if (pi->map_mask) gdk_pixmap_unref(pi->map_mask);
229  if (pi->map_image) {
230 #ifdef HAVE_SDL
232  free(((SDL_Surface*)pi->map_image)->pixels);
233  SDL_FreeSurface(pi->map_image);
234  SDL_FreeSurface(pi->fog_image);
235 /* free(((SDL_Surface*)pi->fog_image)->pixels);*/
236  }
237  else
238 #endif
239  {
240  gdk_pixmap_unref(pi->map_image);
241  }
242  }
243 }
244 
245 /* Takes the pixmap to put the data into, as well as the rgba
246  * data (ie, already loaded with png_to_data). Scales and
247  * stores the relevant data into the pixmap structure.
248  * returns 1 on failure.
249  * ce can be NULL
250  */
251 
252 int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height)
253 {
254  int nx, ny, iscale, factor;
255  uint8 *png_tmp;
256  PixmapInfo *pi;
257 
258  if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM)
259  return 1;
260 
261  if (pixmaps[pixmap_num] != pixmaps[0]) {
262  free_pixmap(pixmaps[pixmap_num]);
263  free(pixmaps[pixmap_num]);
264  pixmaps[pixmap_num] = pixmaps[0];
265  }
266 
267  pi = calloc(1, sizeof(PixmapInfo));
268 
269  iscale = use_config[CONFIG_ICONSCALE];
270 
271  /* If the image is big, figure out what we should scale it to so it fits better display */
272  if (width > DEFAULT_IMAGE_SIZE || height>DEFAULT_IMAGE_SIZE) {
273  int ts = 100;
274 
275  factor = width / DEFAULT_IMAGE_SIZE;
276  if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1;
277  if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor];
278 
279  factor = height / DEFAULT_IMAGE_SIZE;
280  if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1;
281  if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor];
282 
283  iscale = ts * use_config[CONFIG_ICONSCALE] / 100;
284  }
285 
286 
287  /* In all cases, the icon images are in native form. */
288  if (iscale != 100) {
289  nx=width;
290  ny=height;
291  png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, iscale);
292  pi->icon_width = nx;
293  pi->icon_height = ny;
294  create_icon_image(png_tmp, pi, pixmap_num);
295  free(png_tmp);
296  }
297  else {
298  pi->icon_width = width;
299  pi->icon_height = height;
300  create_icon_image(rgba_data, pi, pixmap_num);
301  }
302 
303  /* We could try to be more intelligent if icon_scale matched use_config[CONFIG_MAPSCALE],
304  * but this shouldn't be called too often, and this keeps the code
305  * simpler.
306  *
307  * maybe, but it uses graphical resources, and takes time (don't laugh, some people use
308  * slow old comps! :p)
309  * Ryo 2005-09-05
310  *
311  * Maybe, but only if were using PIXMAP mode - otherwise, SDL doesn't know how to
312  * draw GDK pixmaps.
313  * MSW 2006-11-05
314  */
316  pi->map_height = pi->icon_height;
317  pi->map_width = pi->icon_width;
318  pi->map_mask = pi->icon_mask;
319  pi->map_image = pi->icon_image;
320  gdk_pixmap_ref(pi->map_image);
321  if (pi->map_mask)
322  gdk_pixmap_ref(pi->map_mask);
323  } else if (use_config[CONFIG_MAPSCALE] != 100) {
324  nx=width;
325  ny=height;
326  png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, use_config[CONFIG_MAPSCALE]);
327  pi->map_width = nx;
328  pi->map_height = ny;
329  create_map_image(png_tmp, pi);
330  if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP) free(png_tmp);
331  } else {
332  pi->map_width = width;
333  pi->map_height = height;
334  /* if using SDL mode, a copy of the rgba data needs to be
335  * stored away.
336  */
338  png_tmp = malloc(width * height * BPP);
339  memcpy(png_tmp, rgba_data, width * height * BPP);
340  } else
341  png_tmp = rgba_data;
342  create_map_image(png_tmp, pi);
343  }
344  /* Not ideal, but basically, if it is missing the map or icon image, presume
345  * something failed.
346  */
347  if (!pi->icon_image || !pi->map_image) {
348  free_pixmap(pi);
349  free(pi);
350  return 1;
351  }
352  if (ce) {
353  ce->image_data = pi;
354  redraw_needed=1;
355  }
356  pixmaps[pixmap_num] = pi;
357  return 0;
358 }
359 
360 void addsmooth(uint16 face, uint16 smooth_face)
361 {
362  pixmaps[face]->smooth_face = smooth_face;
363 }
364 
365 /* This functions associates the image_data in the cache entry
366  * with the specific pixmap number. Returns 0 on success, -1
367  * on failure. Currently, there is no failure condition, but
368  * there is the potential that in the future, we want to more
369  * closely look at the data and if it isn't valid, return
370  * the failure code.
371  */
372 int associate_cache_entry(Cache_Entry *ce, int pixnum)
373 {
374 
375  pixmaps[pixnum] = ce->image_data;
376  return 0;
377 }
378 
379 /* We can now connect to different servers, so we need to clear out
380  * any old images. We try to free the data also to prevent memory
381  * leaks.
382  * This could be more clever, ie, if we're caching images and go to
383  * a new server and get a name, we should try to re-arrange our cache
384  * or the like.
385  */
386 
388 {
389  int i;
391 
392  /* The entries in the pixmaps array are also tracked in the image cache in
393  * the common area. We will try to recyle those images that we can - thus, if
394  * we connect to a new server, we can just re-use the images we have already
395  * rendered.
396  */
397  for (i=1; i<MAXPIXMAPNUM; i++) {
398  if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) {
399  free_pixmap(pixmaps[i]);
400  free(pixmaps[i]);
401  }
402  pixmaps[i] = pixmaps[0];
403  }
404 }
405 
406 
407 /* This function draws a little status bar showing where we our
408  * in terms of downloading all the image data.
409  * start is the start value just sent to the server, end is the end
410  * value. total is the total number of images.
411  * A few hacks:
412  * If start is 1, this is the first batch, so it means we need to
413  * create the appropriate status window.
414  * if start = end = total, it means were finished, so destroy
415  * the gui element.
416  */
417 static GtkWidget *pbar=NULL, *pbar_window=NULL;
418 static GtkAdjustment *padj=NULL;
419 void image_update_download_status(int start, int end, int total)
420 {
421  int x, y, wx, wy, w, h;
422 
423  if (start == 1) {
424  padj = (GtkAdjustment*) gtk_adjustment_new (0, 1, total, 0, 0, 0);
425 
426  pbar = gtk_progress_bar_new_with_adjustment(padj);
427  gtk_progress_set_format_string(GTK_PROGRESS(pbar), "Downloading image %v of %u (%p%% complete)");
428  gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(pbar), GTK_PROGRESS_CONTINUOUS);
429  gtk_progress_set_show_text(GTK_PROGRESS(pbar), TRUE);
430  get_window_coord(gtkwin_root, &x,&y, &wx,&wy,&w,&h);
431 
432  pbar_window = gtk_window_new(GTK_WINDOW_POPUP);
433  gtk_window_set_policy(GTK_WINDOW(pbar_window), TRUE, TRUE, FALSE);
434  gtk_window_set_transient_for(GTK_WINDOW(pbar_window), GTK_WINDOW (gtkwin_root));
435  /* we more or less want this window centered on the main crossfire window,
436  * and not necessarily centered on the screen or in the upper left corner.
437  */
438  gtk_widget_set_uposition(pbar_window, (wx + w)/2, (wy + h) / 2);
439 
440  gtk_container_add(GTK_CONTAINER(pbar_window), pbar);
441  gtk_widget_show(pbar);
442  gtk_widget_show(pbar_window);
443  }
444  if (start == total) {
445  gtk_widget_destroy(pbar_window);
446  pbar = NULL;
447  pbar_window = NULL;
448  padj = NULL;
449  return;
450  }
451 
452  gtk_progress_set_value(GTK_PROGRESS(pbar), start);
453  while ( gtk_events_pending() ) {
454  gtk_main_iteration();
455  }
456 
457 }
458 
459 void get_map_image_size(int face, uint8 *w, uint8 *h)
460 {
461  /* We want to calculate the number of spaces this image
462  * uses it. By adding the image size but substracting one,
463  * we cover the cases where the image size is not an even
464  * increment. EG, if the map_image_size is 32, and an image
465  * is 33 wide, we want that to register as two spaces. By
466  * adding 31, that works out.
467  */
468  if (face < 0 || face >= MAXPIXMAPNUM) {
469  *w = 1;
470  *h = 1;
471  } else {
472  *w = (pixmaps[face]->map_width + map_image_size - 1)/ map_image_size;
473  *h = (pixmaps[face]->map_height + map_image_size - 1)/ map_image_size;
474  }
475 }
static void free_pixmap(PixmapInfo *pi)
Definition: image.c:224
struct Keys * next
Definition: image.c:104
#define DEFAULT_IMAGE_SIZE
Definition: gx11.c:114
#define BPP
Definition: image.c:67
int last_face_num
Definition: image.c:69
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
struct Keys Key_Entry
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
#define MAX_ICON_SPACES
Definition: image.c:86
uint8 redraw_needed
Definition: gx11.c:205
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
uint32 checksum
Definition: client.h:482
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
void addsmooth(uint16 face, uint16 smooth_face)
Definition: image.c:360
uint8 * png_data
Definition: image.c:63
sint8 direction
Definition: image.c:101
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
void * map_mask
Definition: gx11.h:63
void * icon_image
Definition: gx11.h:61
GtkWidget * gtkwin_root
Definition: gx11.c:278
#define CONFIG_CACHE
Definition: client.h:156
char * name
Definition: image.c:61
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
char * command
Definition: image.c:103
static GtkAdjustment * padj
Definition: image.c:418
uint32 height
Definition: image.c:64
static GtkWidget * pbar
Definition: image.c:417
#define CFG_DM_PIXMAP
Definition: client.h:194
unsigned int uint32
Definition: client-types.h:77
static void create_map_image(uint8 *data, PixmapInfo *pi)
Definition: image.c:123
static GtkWidget * pbar_window
Definition: image.c:417
static int icon_rescale_factor[MAX_ICON_SPACES]
Definition: image.c:87
#define CONFIG_ICONSCALE
Definition: client.h:158
static void create_icon_image(uint8 *data, PixmapInfo *pi, int pixmap_num)
Definition: image.c:114
signed char sint8
Definition: client-types.h:82
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
uint16 smooth_face
Definition: gx11.h:66
uint8 flags
Definition: image.c:100
#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
KeySym keysym
Definition: image.c:102
const char *const rcsid_gtk_image_c
Definition: image.c:1
Definition: image.c:99
uint16 map_width
Definition: gx11.h:64
void * icon_mask
Definition: gx11.h:61