|
Crossfire Client, Trunk
R18666
|
00001 const char * const rcsid_gtk2_image_c = 00002 "$Id: image.c 12987 2010-04-27 03:50:58Z kbulgrien $"; 00003 /* 00004 Crossfire client, a client program for the crossfire program. 00005 00006 Copyright (C) 2005-2008,2010 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@metalforge.org 00023 */ 00024 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 #include <glade/glade.h> 00043 #ifndef WIN32 00044 #include <gdk/gdkx.h> 00045 #else 00046 #include <gdk/gdkwin32.h> 00047 #endif 00048 #include <gdk/gdkkeysyms.h> 00049 00050 #ifdef HAVE_SDL 00051 #include <SDL.h> 00052 #include <SDL_image.h> 00053 #endif 00054 00055 #include "client-types.h" 00056 #include "client.h" 00057 #include "image.h" 00058 #include "main.h" 00059 #include "mapdata.h" 00060 #include "gtk2proto.h" 00061 00062 extern GtkWidget *window_root; 00063 int image_size=DEFAULT_IMAGE_SIZE; 00064 00065 struct { 00066 char *name; 00067 uint32 checksum; 00068 uint8 *png_data; 00069 uint32 width, height; 00070 } private_cache[MAXPIXMAPNUM]; 00071 00072 #define BPP 4 00073 00074 PixmapInfo *pixmaps[MAXPIXMAPNUM]; 00075 00076 int last_face_num=0; 00077 00078 /* Do we have new images to display? */ 00079 int have_new_image=0; 00080 00081 /* 00082 * this is used to rescale big images that will be drawn in the inventory/look 00083 * lists. What the code further below basically does is figure out how big the 00084 * object is (in squares), and this looks at the icon_rescale_factor to figure 00085 * what scale factor it gives. Not that the icon_rescale_factor values are 00086 * passed directly to the rescale routines. These represent percentages - so 00087 * even taking into account that the values diminish as the table grows, they 00088 * will still appear larger if the location in the table times the factor is 00089 * greater than 100. We find the largest dimension that the image has. The 00090 * values in the comment is the effective scaling compared to the base image 00091 * size that this big image will appear as. Using a table makes it easier to 00092 * adjust the values so things look right. 00093 */ 00094 00095 #define MAX_ICON_SPACES 10 00096 static const int icon_rescale_factor[MAX_ICON_SPACES] = { 00097 100, 100, 80 /* 2 = 160 */, 60 /* 3 = 180 */, 00098 50 /* 4 = 200 */, 45 /* 5 = 225 */, 40 /* 6 = 240 */, 00099 35 /* 7 = 259 */, 35 /* 8 = 280 */, 33 /* 9 = 300 */ 00100 }; 00101 00102 /****************************************************************************** 00103 * 00104 * Code related to face caching. 00105 * 00106 *****************************************************************************/ 00107 00108 /* Does not appear to be used anywhere 00109 typedef struct Keys { 00110 uint8 flags; 00111 sint8 direction; 00112 KeySym keysym; 00113 char *command; 00114 struct Keys *next; 00115 } Key_Entry; 00116 */ 00117 00118 /* Rotate right from bsd sum. */ 00119 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1; 00120 00121 /*#define CHECKSUM_DEBUG*/ 00122 00130 static void create_icon_image(uint8 *data, PixmapInfo *pi, int pixmap_num) 00131 { 00132 pi->icon_mask = NULL; 00133 if (rgba_to_gdkpixbuf(data, pi->icon_width, pi->icon_height, 00134 (GdkPixbuf**)&pi->icon_image)) 00135 LOG (LOG_ERROR,"gtk-v2::create_icon_image","Unable to create scaled image, dest num = %d\n", pixmap_num); 00136 } 00137 00144 static void create_map_image(uint8 *data, PixmapInfo *pi) 00145 { 00146 pi->map_image = NULL; 00147 pi->map_mask = NULL; 00148 00149 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00150 #if defined(HAVE_SDL) 00151 int i; 00152 SDL_Surface *fog; 00153 uint32 g,*p; 00154 uint8 *l; 00155 00156 #if SDL_BYTEORDER == SDL_LIL_ENDIAN 00157 pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width, 00158 pi->map_height, 32, pi->map_width * 4, 0xff, 00159 0xff00, 0xff0000, 0xff000000); 00160 00161 fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE, 00162 pi->map_width, pi->map_height, 32, 0xff, 00163 0xff00, 0xff0000, 0xff000000); 00164 SDL_LockSurface(fog); 00165 00166 for (i=0; i < pi->map_width * pi->map_height; i++) { 00167 l = (uint8 *) (data + i*4); 00168 #if 1 00169 g = MAX(*l, *(l+1)); 00170 g = MAX(g, *(l+2)); 00171 #else 00172 g = ( *l + *(l+1) + *(l+2)) / 3; 00173 #endif 00174 p = (uint32*) fog->pixels + i; 00175 *p = g | (g << 8) | (g << 16) | (*(l + 3) << 24); 00176 } 00177 00178 SDL_UnlockSurface(fog); 00179 pi->fog_image = fog; 00180 #else 00181 /* Big endian */ 00182 pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width, 00183 pi->map_height, 32, pi->map_width * 4, 0xff000000, 00184 0xff0000, 0xff00, 0xff); 00185 00186 fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE, 00187 pi->map_width, pi->map_height, 32, 0xff000000, 00188 0xff0000, 0xff00, 0xff); 00189 SDL_LockSurface(fog); 00190 00191 /* 00192 * I think this works out, but haven't tried it on a big endian machine 00193 * as my recollection is that the png data would be in the same order, 00194 * just the bytes for it to go on the screen are reversed. 00195 */ 00196 for (i=0; i < pi->map_width * pi->map_height; i++) { 00197 l = (uint8 *) (data + i*4); 00198 #if 1 00199 g = MAX(*l, *(l+1)); 00200 g = MAX(g, *(l+2)); 00201 #else 00202 g = ( *l + *(l+1) + *(l+2)) / 3; 00203 #endif 00204 p = (uint32*) fog->pixels + i; 00205 *p = (g << 8) | (g << 16) | (g << 24) | *(l + 3); 00206 } 00207 00208 for (i=0; i < pi->map_width * pi->map_height; i+= 4) { 00209 uint32 *tmp; 00210 00211 /* 00212 * The pointer arithemtic below looks suspicious, but it is a patch 00213 * that is submitted, so just putting it in as submitted. MSW 00214 * 2004-05-11 00215 */ 00216 p = (uint32*) (fog->pixels + i); 00217 g = ( ((*p >> 24) & 0xff) + ((*p >> 16) & 0xff) + ((*p >> 8) & 0xff)) / 3; 00218 tmp = (uint32*) fog->pixels + i; 00219 *tmp = (g << 24) | (g << 16) | (g << 8) | (*p & 0xff); 00220 } 00221 00222 SDL_UnlockSurface(fog); 00223 pi->fog_image = fog; 00224 #endif 00225 00226 #endif 00227 } 00228 else if (use_config[CONFIG_DISPLAYMODE] == CFG_DM_OPENGL){ 00229 #ifdef HAVE_OPENGL 00230 create_opengl_map_image(data, pi); 00231 #endif 00232 } 00233 00234 else if (use_config[CONFIG_DISPLAYMODE] == CFG_DM_PIXMAP){ 00235 rgba_to_gdkpixmap(window_root->window, data, pi->map_width, pi->map_height, 00236 (GdkPixmap**)&pi->map_image, (GdkBitmap**)&pi->map_mask, 00237 gtk_widget_get_colormap(window_root)); 00238 } 00239 } 00240 00246 static void free_pixmap(PixmapInfo *pi) 00247 { 00248 if (pi->icon_image) g_object_unref(pi->icon_image); 00249 if (pi->icon_mask) g_object_unref(pi->icon_mask); 00250 if (pi->map_mask) gdk_pixmap_unref(pi->map_mask); 00251 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00252 #ifdef HAVE_SDL 00253 if (pi->map_image) { 00254 SDL_FreeSurface(pi->map_image); 00255 free(((SDL_Surface*)pi->map_image)->pixels); 00256 SDL_FreeSurface(pi->fog_image); 00257 /* 00258 * Minor memory leak here - SDL_FreeSurface() frees the pixel 00259 * data _unless_ SDL_CreateRGBSurfaceFrom() was used to create 00260 * the surface. SDL_CreateRGBSurfaceFrom() is used to create 00261 * the map data, which is why we need the free there. The 00262 * reason this is a minor memory leak is because 00263 * SDL_CreateRGBSurfaceFrom() is used to create the question 00264 * mark image, and without this free, that data is not freed. 00265 * However, with this, client crashes after disconnecting from 00266 * server with double free. 00267 */ 00268 /* free(((SDL_Surface*)pi->fog_image)->pixels);*/ 00269 } 00270 #endif 00271 } 00272 else if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_OPENGL) { 00273 #ifdef HAVE_OPENGL 00274 opengl_free_pixmap(pi); 00275 #endif 00276 } 00277 else if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP) { 00278 if (pi->map_image) { 00279 gdk_pixmap_unref(pi->map_image); 00280 } 00281 } 00282 } 00283 00297 int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height) 00298 { 00299 int nx, ny, iscale, factor; 00300 uint8 *png_tmp; 00301 PixmapInfo *pi; 00302 00303 if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM) 00304 return 1; 00305 00306 if (pixmaps[pixmap_num] != pixmaps[0]) { 00307 /* As per bug 2938906, one can see image corruption when switching between 00308 * servers. The cause is that the cache table stores away 00309 * a pointer to the pixmap[] entry - if we go and free it, 00310 * the cache table can point to garbage, so don't free it. 00311 * This causes some memory leak, but if/when there is good 00312 * cache support for multiple servers, eventually the amount 00313 * of memory consumed will reach a limit (it has every image of 00314 * every server in memory 00315 * 00316 * The cause of image corruption requires a few different things: 00317 * 1) images of the same name have different numbers on the 2 serves. 00318 * 2) the image number is higher on the first than second server 00319 * 3) the image using the high number does not exist/is different 00320 * on the second server, causing this routine to be called. 00321 */ 00322 00323 if (!use_config[CONFIG_CACHE]) { 00324 free_pixmap(pixmaps[pixmap_num]); 00325 free(pixmaps[pixmap_num]); 00326 } 00327 pixmaps[pixmap_num] = pixmaps[0]; 00328 } 00329 00330 pi = calloc(1, sizeof(PixmapInfo)); 00331 00332 iscale = use_config[CONFIG_ICONSCALE]; 00333 00334 /* 00335 * If the image is big, figure out what we should scale it to so it fits 00336 * better display 00337 */ 00338 if (width > DEFAULT_IMAGE_SIZE || height>DEFAULT_IMAGE_SIZE) { 00339 int ts = 100; 00340 00341 factor = width / DEFAULT_IMAGE_SIZE; 00342 if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1; 00343 if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor]; 00344 00345 factor = height / DEFAULT_IMAGE_SIZE; 00346 if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1; 00347 if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor]; 00348 00349 iscale = ts * use_config[CONFIG_ICONSCALE] / 100; 00350 } 00351 00352 /* In all cases, the icon images are in native form. */ 00353 if (iscale != 100) { 00354 nx=width; 00355 ny=height; 00356 png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, iscale); 00357 pi->icon_width = nx; 00358 pi->icon_height = ny; 00359 create_icon_image(png_tmp, pi, pixmap_num); 00360 free(png_tmp); 00361 } 00362 else { 00363 pi->icon_width = width; 00364 pi->icon_height = height; 00365 create_icon_image(rgba_data, pi, pixmap_num); 00366 } 00367 00368 /* 00369 * If icon_scale matched use_config[CONFIG_MAPSCALE], we could try to be 00370 * more intelligent, but this should not be called too often, and this 00371 * keeps the code simpler. 00372 */ 00373 if (use_config[CONFIG_MAPSCALE] != 100) { 00374 nx=width; 00375 ny=height; 00376 png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, use_config[CONFIG_MAPSCALE]); 00377 pi->map_width = nx; 00378 pi->map_height = ny; 00379 create_map_image(png_tmp, pi); 00380 /* 00381 * pixmap mode and opengl don't need the rgba data after they have 00382 * created the image, so we can free it. SDL uses the raw rgba data, 00383 * so it can't be freed. 00384 */ 00385 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP || 00386 use_config[CONFIG_DISPLAYMODE]==CFG_DM_OPENGL) free(png_tmp); 00387 } else { 00388 pi->map_width = width; 00389 pi->map_height = height; 00390 /* 00391 * If using SDL mode, a copy of the rgba data needs to be stored away. 00392 */ 00393 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00394 png_tmp = malloc(width * height * BPP); 00395 memcpy(png_tmp, rgba_data, width * height * BPP); 00396 } else 00397 png_tmp = rgba_data; 00398 create_map_image(png_tmp, pi); 00399 } 00400 /* 00401 * Not ideal, but if it is missing the map or icon image, presume something 00402 * failed. However, opengl doesn't set the map_image, so if using that 00403 * display mode, don't make this check. 00404 */ 00405 if (!pi->icon_image || (!pi->map_image && use_config[CONFIG_DISPLAYMODE]!=CFG_DM_OPENGL)) { 00406 free_pixmap(pi); 00407 free(pi); 00408 return 1; 00409 } 00410 if (ce) { 00411 ce->image_data = pi; 00412 } 00413 pixmaps[pixmap_num] = pi; 00414 if (use_config[CONFIG_CACHE]) 00415 have_new_image++; 00416 00417 return 0; 00418 } 00419 00426 void addsmooth(uint16 face, uint16 smooth_face) 00427 { 00428 pixmaps[face]->smooth_face = smooth_face; 00429 } 00430 00439 int associate_cache_entry(Cache_Entry *ce, int pixnum) 00440 { 00441 pixmaps[pixnum] = ce->image_data; 00442 return 0; 00443 } 00444 00451 void reset_image_data(void) 00452 { 00453 int i; 00454 00455 reset_image_cache_data(); 00456 /* 00457 * The entries in the pixmaps array are also tracked in the image cache in 00458 * the common area. We will try to recycle those images that we can. 00459 * Thus, if we connect to a new server, we can just re-use the images we 00460 * have already rendered. 00461 */ 00462 for (i=1; i<MAXPIXMAPNUM; i++) { 00463 if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) { 00464 free_pixmap(pixmaps[i]); 00465 free(pixmaps[i]); 00466 pixmaps[i] = pixmaps[0]; 00467 } 00468 } 00469 } 00470 00471 static GtkWidget *pbar=NULL; 00472 static GtkWidget *pbar_window=NULL; 00473 static GtkAdjustment *padj=NULL; 00474 00486 void image_update_download_status(int start, int end, int total) 00487 { 00488 int x, y, wx, wy, w, h; 00489 00490 if (start == 1) { 00491 padj = (GtkAdjustment*) gtk_adjustment_new (0, 1, total, 0, 0, 0); 00492 00493 pbar = gtk_progress_bar_new_with_adjustment(padj); 00494 gtk_progress_set_format_string(GTK_PROGRESS(pbar), "Downloading image %v of %u (%p%% complete)"); 00495 gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(pbar), GTK_PROGRESS_CONTINUOUS); 00496 gtk_progress_set_show_text(GTK_PROGRESS(pbar), TRUE); 00497 get_window_coord(window_root, &x,&y, &wx,&wy,&w,&h); 00498 00499 pbar_window = gtk_window_new(GTK_WINDOW_POPUP); 00500 gtk_window_set_policy(GTK_WINDOW(pbar_window), TRUE, TRUE, FALSE); 00501 gtk_window_set_transient_for(GTK_WINDOW(pbar_window), GTK_WINDOW (window_root)); 00502 /* 00503 * We more or less want this window centered on the main crossfire 00504 * window, and not necessarily centered on the screen or in the upper 00505 * left corner. 00506 */ 00507 gtk_widget_set_uposition(pbar_window, (wx + w)/2, (wy + h) / 2); 00508 00509 gtk_container_add(GTK_CONTAINER(pbar_window), pbar); 00510 gtk_widget_show(pbar); 00511 gtk_widget_show(pbar_window); 00512 } 00513 if (start == total) { 00514 gtk_widget_destroy(pbar_window); 00515 pbar = NULL; 00516 pbar_window = NULL; 00517 padj = NULL; 00518 return; 00519 } 00520 00521 gtk_progress_set_value(GTK_PROGRESS(pbar), start); 00522 while ( gtk_events_pending() ) { 00523 gtk_main_iteration(); 00524 } 00525 } 00526 00533 void get_map_image_size(int face, uint8 *w, uint8 *h) 00534 { 00535 /* We want to calculate the number of spaces this image 00536 * uses it. By adding the image size but substracting one, 00537 * we cover the cases where the image size is not an even 00538 * increment. EG, if the map_image_size is 32, and an image 00539 * is 33 wide, we want that to register as two spaces. By 00540 * adding 31, that works out. 00541 */ 00542 if ( face < 0 || face >= MAXPIXMAPNUM) { 00543 *w = 1; 00544 *h = 1; 00545 } else { 00546 *w = (pixmaps[face]->map_width + map_image_size - 1)/ map_image_size; 00547 *h = (pixmaps[face]->map_height + map_image_size - 1)/ map_image_size; 00548 } 00549 } 00550 00551 /****************************************************************************** 00552 * 00553 * Code related to face caching. 00554 * 00555 *****************************************************************************/ 00556 00566 void init_image_cache_data(void) 00567 { 00568 int i; 00569 GtkStyle *style; 00570 #include "../../pixmaps/question.xpm" 00571 00572 00573 LOG(LOG_INFO,"gtk-v2::init_image_cache_data","Init Image Cache"); 00574 00575 style = gtk_widget_get_style(window_root); 00576 pixmaps[0] = malloc(sizeof(PixmapInfo)); 00577 pixmaps[0]->icon_image = gdk_pixmap_create_from_xpm_d(window_root->window, 00578 (GdkBitmap**)&pixmaps[0]->icon_mask, 00579 &style->bg[GTK_STATE_NORMAL], 00580 (gchar **)question_xpm); 00581 #ifdef HAVE_SDL 00582 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00583 /* 00584 * Make a semi-transparent question mark symbol to use for the cached 00585 * images. 00586 */ 00587 #include "../../pixmaps/question.sdl" 00588 pixmaps[0]->map_image = SDL_CreateRGBSurfaceFrom(question_sdl, 00589 32, 32, 1, 4, 1, 1, 1, 1); 00590 SDL_SetAlpha(pixmaps[0]->map_image, SDL_SRCALPHA, 70); 00591 pixmaps[0]->fog_image = SDL_CreateRGBSurfaceFrom(question_sdl, 00592 32, 32, 1, 4, 1, 1, 1, 1); 00593 SDL_SetAlpha(pixmaps[0]->fog_image, SDL_SRCALPHA, 70); 00594 } 00595 else 00596 #endif 00597 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP) 00598 { 00599 pixmaps[0]->map_image = pixmaps[0]->icon_image; 00600 pixmaps[0]->fog_image = pixmaps[0]->icon_image; 00601 pixmaps[0]->map_mask = pixmaps[0]->icon_mask; 00602 } 00603 #ifdef HAVE_OPENGL 00604 else if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_OPENGL) { 00605 create_opengl_question_mark(); 00606 } 00607 #endif 00608 00609 pixmaps[0]->icon_width = pixmaps[0]->icon_height = pixmaps[0]->map_width = pixmaps[0]->map_height = map_image_size; 00610 pixmaps[0]->smooth_face = 0; 00611 00612 /* Don't do anything special for SDL image - rather, that drawing 00613 * code will check to see if there is no data 00614 */ 00615 00616 /* Initialize all the images to be of the same value. */ 00617 for (i=1; i<MAXPIXMAPNUM; i++) { 00618 pixmaps[i] = pixmaps[0]; 00619 } 00620 00621 init_common_cache_data(); 00622 }
1.7.6.1