Crossfire Client, Branch
R11627
|
00001 const char * const rcsid_gtk_image_c = 00002 "$Id: image.c 9201 2008-06-01 17:32:45Z anmaster $"; 00003 /* 00004 Crossfire client, a client program for the crossfire program. 00005 00006 Copyright (C) 2001 Mark Wedel & Crossfire Development Team 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 2 of the License, or 00011 (at your option) any later version. 00012 00013 This program is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with this program; if not, write to the Free Software 00020 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00021 00022 The author can be reached via e-mail to crossfire-devel@real-time.com 00023 */ 00024 00025 /* 00026 * This file contains image related functions - this is a higher level up - 00027 * it mostly deals with the caching of the images, processing the image commands 00028 * from the server, etc. This file is gtk specific - at least it returns 00029 * gtk pixmaps. 00030 */ 00031 00032 #include <config.h> 00033 #include <stdlib.h> 00034 #include <sys/stat.h> 00035 #ifndef WIN32 00036 #include <unistd.h> 00037 #endif 00038 #include <png.h> 00039 00040 /* Pick up the gtk headers we need */ 00041 #include <gtk/gtk.h> 00042 #ifndef WIN32 00043 #include <gdk/gdkx.h> 00044 #else 00045 #include <gdk/gdkwin32.h> 00046 #endif 00047 #include <gdk/gdkkeysyms.h> 00048 00049 #ifdef HAVE_SDL 00050 #include <SDL.h> 00051 #include <SDL_image.h> 00052 #endif 00053 00054 #include "client-types.h" 00055 #include "gx11.h" 00056 #include "client.h" 00057 00058 #include "gtkproto.h" 00059 00060 struct { 00061 char *name; 00062 uint32 checksum; 00063 uint8 *png_data; 00064 uint32 width, height; 00065 } private_cache[MAXPIXMAPNUM]; 00066 00067 #define BPP 4 00068 00069 int last_face_num=0; 00070 00071 /* this is used to rescale big images that will be drawn in the inventory/look 00072 * lists. What the code further below basically does is figure out how big 00073 * the object is (in squares), and this looks at the icon_rescale_factor 00074 * to figure what scale factor it gives. Not that the icon_rescale_factor 00075 * values are passed directly to the rescale routines. These represent 00076 * percentages - so even taking into account that the values diminish 00077 * as the table grows, they will still appear larger if the location 00078 * in the table times the factor is greater than 100. We find 00079 * the largest dimension that the image has. The values in the comment 00080 * is the effective scaling compared to the base image size that this 00081 * big image will appear as. 00082 * Using a table makes it easier to adjust the values so things 00083 * look right. 00084 */ 00085 00086 #define MAX_ICON_SPACES 10 00087 static int icon_rescale_factor[MAX_ICON_SPACES] = { 00088 100, 100, 80 /* 2 = 160 */, 60 /* 3 = 180 */, 00089 50 /* 4 = 200 */, 45 /* 5 = 225 */, 40 /* 6 = 240 */, 00090 35 /* 7 = 259 */, 35 /* 8 = 280 */, 33 /* 9 = 300 */ 00091 }; 00092 00093 /****************************************************************************** 00094 * 00095 * Code related to face caching. 00096 * 00097 *****************************************************************************/ 00098 00099 typedef struct Keys { 00100 uint8 flags; 00101 sint8 direction; 00102 KeySym keysym; 00103 char *command; 00104 struct Keys *next; 00105 } Key_Entry; 00106 00107 00108 /* Rotate right from bsd sum. */ 00109 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1; 00110 00111 /*#define CHECKSUM_DEBUG*/ 00112 00113 /* These little helper functions just make the code below much more readable */ 00114 static void create_icon_image(uint8 *data, PixmapInfo *pi, int pixmap_num) 00115 { 00116 if (rgba_to_gdkpixmap(gtkwin_root->window, data, pi->icon_width, pi->icon_height, 00117 (GdkPixmap**)&pi->icon_image, (GdkBitmap**)&pi->icon_mask, 00118 gtk_widget_get_colormap(gtkwin_root))) 00119 LOG (LOG_ERROR,"gtk::create_icon_image","Unable to create scaled image, dest num = %d\n", pixmap_num); 00120 } 00121 00122 /* These little helper functions just make the code below much more readable */ 00123 static void create_map_image(uint8 *data, PixmapInfo *pi) 00124 { 00125 pi->map_image = NULL; 00126 pi->map_mask = NULL; 00127 00128 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00129 #if defined(HAVE_SDL) 00130 int i; 00131 SDL_Surface *fog; 00132 uint32 g,*p; 00133 uint8 *l; 00134 00135 #if SDL_BYTEORDER == SDL_LIL_ENDIAN 00136 pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width, 00137 pi->map_height, 32, pi->map_width * 4, 0xff, 00138 0xff00, 0xff0000, 0xff000000); 00139 00140 fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE, 00141 pi->map_width, pi->map_height, 32, 0xff, 00142 0xff00, 0xff0000, 0xff000000); 00143 SDL_LockSurface(fog); 00144 00145 for (i=0; i < pi->map_width * pi->map_height; i++) { 00146 l = (uint8 *) (data + i*4); 00147 /* The effective luminance of a pixel is calculated with the following formula: 00148 * Y=0.3RED+0.59GREEN+0.11Blue 00149 * This is the way you are supposed to convert color to grayscale 00150 * for best 'natural' results. Using maxes, mins or simple mean is 00151 * gives awfull results with darkness code. 00152 * For speed reason i work here in integers 00153 * (77RED+151GREEN+28BLUE)>>8 00154 * 00155 * Tchize 22 May 2004 00156 */ 00157 g=(77*(*l)+151*(*(l+1))+28*(*(l+2)))>>8; 00158 p = (uint32*) fog->pixels + i; 00159 *p = g | (g << 8) | (g << 16) | (*(l + 3) << 24); 00160 } 00161 00162 SDL_UnlockSurface(fog); 00163 pi->fog_image = fog; 00164 #else 00165 /* Big endian */ 00166 pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width, 00167 pi->map_height, 32, pi->map_width * 4, 0xff000000, 00168 0xff0000, 0xff00, 0xff); 00169 00170 fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE, 00171 pi->map_width, pi->map_height, 32, 0xff000000, 00172 0xff0000, 0xff00, 0xff); 00173 SDL_LockSurface(fog); 00174 00175 /* I think this works out, but haven't tried it on a big 00176 * endian machine - my recollection is that the png data 00177 * would be in the same order, just the bytes for it to go 00178 * on teh screen are reversed. 00179 */ 00180 for (i=0; i < pi->map_width * pi->map_height; i++) { 00181 l = (uint8 *) (data + i*4); 00182 /* The effective luminance of a pixel is calculated with the following formula: 00183 * Y=0.3RED+0.59GREEN+0.11Blue 00184 * This is the way you are supposed to convert color to grayscale 00185 * for best 'natural' results. Using maxes, mins or simple mean is 00186 * gives awfull results with darkness code. 00187 * For speed reason i work here in integers 00188 * (77RED+151GREEN+28BLUE)>>8 00189 * 00190 * Tchize 22 May 2004 00191 */ 00192 g=(77*(*l)+151*(*(l+1))+28*(*(l+2)))>>8; 00193 p = (uint32*) fog->pixels + i; 00194 *p = (g << 8) | (g << 16) | (g << 24) | *(l + 3); 00195 } 00196 00197 for (i=0; i < pi->map_width * pi->map_height; i+= 4) { 00198 uint32 *tmp; 00199 00200 /* The pointer arithemtic below looks suspicious, but it is a patch that 00201 * is submitted, so just putting it in as submitted. 00202 * MSW 2004-05-11 00203 */ 00204 p = (uint32*) (fog->pixels + i); 00205 g = ( ((*p >> 24) & 0xff) + ((*p >> 16) & 0xff) + ((*p >> 8) & 0xff)) / 3; 00206 tmp = (uint32*) fog->pixels + i; 00207 *tmp = (g << 24) | (g << 16) | (g << 8) | (*p & 0xff); 00208 00209 } 00210 00211 SDL_UnlockSurface(fog); 00212 pi->fog_image = fog; 00213 #endif 00214 00215 #endif 00216 } 00217 else { 00218 rgba_to_gdkpixmap(gtkwin_root->window, data, pi->map_width, pi->map_height, 00219 (GdkPixmap**)&pi->map_image, (GdkBitmap**)&pi->map_mask, 00220 gtk_widget_get_colormap(gtkwin_root)); 00221 } 00222 } 00223 00224 static void free_pixmap(PixmapInfo *pi) 00225 { 00226 if (pi->icon_image) gdk_pixmap_unref(pi->icon_image); 00227 if (pi->icon_mask) gdk_pixmap_unref(pi->icon_mask); 00228 if (pi->map_mask) gdk_pixmap_unref(pi->map_mask); 00229 if (pi->map_image) { 00230 #ifdef HAVE_SDL 00231 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00232 free(((SDL_Surface*)pi->map_image)->pixels); 00233 SDL_FreeSurface(pi->map_image); 00234 SDL_FreeSurface(pi->fog_image); 00235 /* free(((SDL_Surface*)pi->fog_image)->pixels);*/ 00236 } 00237 else 00238 #endif 00239 { 00240 gdk_pixmap_unref(pi->map_image); 00241 } 00242 } 00243 } 00244 00245 /* Takes the pixmap to put the data into, as well as the rgba 00246 * data (ie, already loaded with png_to_data). Scales and 00247 * stores the relevant data into the pixmap structure. 00248 * returns 1 on failure. 00249 * ce can be NULL 00250 */ 00251 00252 int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height) 00253 { 00254 int nx, ny, iscale, factor; 00255 uint8 *png_tmp; 00256 PixmapInfo *pi; 00257 00258 if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM) 00259 return 1; 00260 00261 if (pixmaps[pixmap_num] != pixmaps[0]) { 00262 free_pixmap(pixmaps[pixmap_num]); 00263 free(pixmaps[pixmap_num]); 00264 pixmaps[pixmap_num] = pixmaps[0]; 00265 } 00266 00267 pi = calloc(1, sizeof(PixmapInfo)); 00268 00269 iscale = use_config[CONFIG_ICONSCALE]; 00270 00271 /* If the image is big, figure out what we should scale it to so it fits better display */ 00272 if (width > DEFAULT_IMAGE_SIZE || height>DEFAULT_IMAGE_SIZE) { 00273 int ts = 100; 00274 00275 factor = width / DEFAULT_IMAGE_SIZE; 00276 if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1; 00277 if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor]; 00278 00279 factor = height / DEFAULT_IMAGE_SIZE; 00280 if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1; 00281 if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor]; 00282 00283 iscale = ts * use_config[CONFIG_ICONSCALE] / 100; 00284 } 00285 00286 00287 /* In all cases, the icon images are in native form. */ 00288 if (iscale != 100) { 00289 nx=width; 00290 ny=height; 00291 png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, iscale); 00292 pi->icon_width = nx; 00293 pi->icon_height = ny; 00294 create_icon_image(png_tmp, pi, pixmap_num); 00295 free(png_tmp); 00296 } 00297 else { 00298 pi->icon_width = width; 00299 pi->icon_height = height; 00300 create_icon_image(rgba_data, pi, pixmap_num); 00301 } 00302 00303 /* We could try to be more intelligent if icon_scale matched use_config[CONFIG_MAPSCALE], 00304 * but this shouldn't be called too often, and this keeps the code 00305 * simpler. 00306 * 00307 * maybe, but it uses graphical resources, and takes time (don't laugh, some people use 00308 * slow old comps! :p) 00309 * Ryo 2005-09-05 00310 * 00311 * Maybe, but only if were using PIXMAP mode - otherwise, SDL doesn't know how to 00312 * draw GDK pixmaps. 00313 * MSW 2006-11-05 00314 */ 00315 if (iscale == use_config[CONFIG_MAPSCALE] && use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP) { 00316 pi->map_height = pi->icon_height; 00317 pi->map_width = pi->icon_width; 00318 pi->map_mask = pi->icon_mask; 00319 pi->map_image = pi->icon_image; 00320 gdk_pixmap_ref(pi->map_image); 00321 if (pi->map_mask) 00322 gdk_pixmap_ref(pi->map_mask); 00323 } else if (use_config[CONFIG_MAPSCALE] != 100) { 00324 nx=width; 00325 ny=height; 00326 png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, use_config[CONFIG_MAPSCALE]); 00327 pi->map_width = nx; 00328 pi->map_height = ny; 00329 create_map_image(png_tmp, pi); 00330 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP) free(png_tmp); 00331 } else { 00332 pi->map_width = width; 00333 pi->map_height = height; 00334 /* if using SDL mode, a copy of the rgba data needs to be 00335 * stored away. 00336 */ 00337 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00338 png_tmp = malloc(width * height * BPP); 00339 memcpy(png_tmp, rgba_data, width * height * BPP); 00340 } else 00341 png_tmp = rgba_data; 00342 create_map_image(png_tmp, pi); 00343 } 00344 /* Not ideal, but basically, if it is missing the map or icon image, presume 00345 * something failed. 00346 */ 00347 if (!pi->icon_image || !pi->map_image) { 00348 free_pixmap(pi); 00349 free(pi); 00350 return 1; 00351 } 00352 if (ce) { 00353 ce->image_data = pi; 00354 redraw_needed=1; 00355 } 00356 pixmaps[pixmap_num] = pi; 00357 return 0; 00358 } 00359 00360 void addsmooth(uint16 face, uint16 smooth_face) 00361 { 00362 pixmaps[face]->smooth_face = smooth_face; 00363 } 00364 00365 /* This functions associates the image_data in the cache entry 00366 * with the specific pixmap number. Returns 0 on success, -1 00367 * on failure. Currently, there is no failure condition, but 00368 * there is the potential that in the future, we want to more 00369 * closely look at the data and if it isn't valid, return 00370 * the failure code. 00371 */ 00372 int associate_cache_entry(Cache_Entry *ce, int pixnum) 00373 { 00374 00375 pixmaps[pixnum] = ce->image_data; 00376 return 0; 00377 } 00378 00379 /* We can now connect to different servers, so we need to clear out 00380 * any old images. We try to free the data also to prevent memory 00381 * leaks. 00382 * This could be more clever, ie, if we're caching images and go to 00383 * a new server and get a name, we should try to re-arrange our cache 00384 * or the like. 00385 */ 00386 00387 void reset_image_data(void) 00388 { 00389 int i; 00390 reset_image_cache_data(); 00391 00392 /* The entries in the pixmaps array are also tracked in the image cache in 00393 * the common area. We will try to recyle those images that we can - thus, if 00394 * we connect to a new server, we can just re-use the images we have already 00395 * rendered. 00396 */ 00397 for (i=1; i<MAXPIXMAPNUM; i++) { 00398 if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) { 00399 free_pixmap(pixmaps[i]); 00400 free(pixmaps[i]); 00401 } 00402 pixmaps[i] = pixmaps[0]; 00403 } 00404 } 00405 00406 00407 /* This function draws a little status bar showing where we our 00408 * in terms of downloading all the image data. 00409 * start is the start value just sent to the server, end is the end 00410 * value. total is the total number of images. 00411 * A few hacks: 00412 * If start is 1, this is the first batch, so it means we need to 00413 * create the appropriate status window. 00414 * if start = end = total, it means were finished, so destroy 00415 * the gui element. 00416 */ 00417 static GtkWidget *pbar=NULL, *pbar_window=NULL; 00418 static GtkAdjustment *padj=NULL; 00419 void image_update_download_status(int start, int end, int total) 00420 { 00421 int x, y, wx, wy, w, h; 00422 00423 if (start == 1) { 00424 padj = (GtkAdjustment*) gtk_adjustment_new (0, 1, total, 0, 0, 0); 00425 00426 pbar = gtk_progress_bar_new_with_adjustment(padj); 00427 gtk_progress_set_format_string(GTK_PROGRESS(pbar), "Downloading image %v of %u (%p%% complete)"); 00428 gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(pbar), GTK_PROGRESS_CONTINUOUS); 00429 gtk_progress_set_show_text(GTK_PROGRESS(pbar), TRUE); 00430 get_window_coord(gtkwin_root, &x,&y, &wx,&wy,&w,&h); 00431 00432 pbar_window = gtk_window_new(GTK_WINDOW_POPUP); 00433 gtk_window_set_policy(GTK_WINDOW(pbar_window), TRUE, TRUE, FALSE); 00434 gtk_window_set_transient_for(GTK_WINDOW(pbar_window), GTK_WINDOW (gtkwin_root)); 00435 /* we more or less want this window centered on the main crossfire window, 00436 * and not necessarily centered on the screen or in the upper left corner. 00437 */ 00438 gtk_widget_set_uposition(pbar_window, (wx + w)/2, (wy + h) / 2); 00439 00440 gtk_container_add(GTK_CONTAINER(pbar_window), pbar); 00441 gtk_widget_show(pbar); 00442 gtk_widget_show(pbar_window); 00443 } 00444 if (start == total) { 00445 gtk_widget_destroy(pbar_window); 00446 pbar = NULL; 00447 pbar_window = NULL; 00448 padj = NULL; 00449 return; 00450 } 00451 00452 gtk_progress_set_value(GTK_PROGRESS(pbar), start); 00453 while ( gtk_events_pending() ) { 00454 gtk_main_iteration(); 00455 } 00456 00457 } 00458 00459 void get_map_image_size(int face, uint8 *w, uint8 *h) 00460 { 00461 /* We want to calculate the number of spaces this image 00462 * uses it. By adding the image size but substracting one, 00463 * we cover the cases where the image size is not an even 00464 * increment. EG, if the map_image_size is 32, and an image 00465 * is 33 wide, we want that to register as two spaces. By 00466 * adding 31, that works out. 00467 */ 00468 if (face < 0 || face >= MAXPIXMAPNUM) { 00469 *w = 1; 00470 *h = 1; 00471 } else { 00472 *w = (pixmaps[face]->map_width + map_image_size - 1)/ map_image_size; 00473 *h = (pixmaps[face]->map_height + map_image_size - 1)/ map_image_size; 00474 } 00475 }