Crossfire Client, Branch
R11627
|
00001 const char * const rcsid_gtk2_image_c = 00002 "$Id: image.c 11043 2008-12-21 04:44:14Z kbulgrien $"; 00003 /* 00004 Crossfire client, a client program for the crossfire program. 00005 00006 Copyright (C) 2005 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 /* 00079 * this is used to rescale big images that will be drawn in the inventory/look 00080 * lists. What the code further below basically does is figure out how big the 00081 * object is (in squares), and this looks at the icon_rescale_factor to figure 00082 * what scale factor it gives. Not that the icon_rescale_factor values are 00083 * passed directly to the rescale routines. These represent percentages - so 00084 * even taking into account that the values diminish as the table grows, they 00085 * will still appear larger if the location in the table times the factor is 00086 * greater than 100. We find the largest dimension that the image has. The 00087 * values in the comment is the effective scaling compared to the base image 00088 * size that this big image will appear as. Using a table makes it easier to 00089 * adjust the values so things look right. 00090 */ 00091 00092 #define MAX_ICON_SPACES 10 00093 static const int icon_rescale_factor[MAX_ICON_SPACES] = { 00094 100, 100, 80 /* 2 = 160 */, 60 /* 3 = 180 */, 00095 50 /* 4 = 200 */, 45 /* 5 = 225 */, 40 /* 6 = 240 */, 00096 35 /* 7 = 259 */, 35 /* 8 = 280 */, 33 /* 9 = 300 */ 00097 }; 00098 00099 /****************************************************************************** 00100 * 00101 * Code related to face caching. 00102 * 00103 *****************************************************************************/ 00104 00105 /* Does not appear to be used anywhere 00106 typedef struct Keys { 00107 uint8 flags; 00108 sint8 direction; 00109 KeySym keysym; 00110 char *command; 00111 struct Keys *next; 00112 } Key_Entry; 00113 */ 00114 00115 /* Rotate right from bsd sum. */ 00116 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1; 00117 00118 /*#define CHECKSUM_DEBUG*/ 00119 00127 static void create_icon_image(uint8 *data, PixmapInfo *pi, int pixmap_num) 00128 { 00129 pi->icon_mask = NULL; 00130 if (rgba_to_gdkpixbuf(data, pi->icon_width, pi->icon_height, 00131 (GdkPixbuf**)&pi->icon_image)) 00132 LOG (LOG_ERROR,"gtk::create_icon_image","Unable to create scaled image, dest num = %d\n", pixmap_num); 00133 } 00134 00141 static void create_map_image(uint8 *data, PixmapInfo *pi) 00142 { 00143 pi->map_image = NULL; 00144 pi->map_mask = NULL; 00145 00146 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00147 #if defined(HAVE_SDL) 00148 int i; 00149 SDL_Surface *fog; 00150 uint32 g,*p; 00151 uint8 *l; 00152 00153 #if SDL_BYTEORDER == SDL_LIL_ENDIAN 00154 pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width, 00155 pi->map_height, 32, pi->map_width * 4, 0xff, 00156 0xff00, 0xff0000, 0xff000000); 00157 00158 fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE, 00159 pi->map_width, pi->map_height, 32, 0xff, 00160 0xff00, 0xff0000, 0xff000000); 00161 SDL_LockSurface(fog); 00162 00163 for (i=0; i < pi->map_width * pi->map_height; i++) { 00164 l = (uint8 *) (data + i*4); 00165 #if 1 00166 g = MAX(*l, *(l+1)); 00167 g = MAX(g, *(l+2)); 00168 #else 00169 g = ( *l + *(l+1) + *(l+2)) / 3; 00170 #endif 00171 p = (uint32*) fog->pixels + i; 00172 *p = g | (g << 8) | (g << 16) | (*(l + 3) << 24); 00173 } 00174 00175 SDL_UnlockSurface(fog); 00176 pi->fog_image = fog; 00177 #else 00178 /* Big endian */ 00179 pi->map_image = SDL_CreateRGBSurfaceFrom(data, pi->map_width, 00180 pi->map_height, 32, pi->map_width * 4, 0xff000000, 00181 0xff0000, 0xff00, 0xff); 00182 00183 fog = SDL_CreateRGBSurface(SDL_SRCALPHA | SDL_HWSURFACE, 00184 pi->map_width, pi->map_height, 32, 0xff000000, 00185 0xff0000, 0xff00, 0xff); 00186 SDL_LockSurface(fog); 00187 00188 /* 00189 * I think this works out, but haven't tried it on a big endian machine 00190 * as my recollection is that the png data would be in the same order, 00191 * just the bytes for it to go on the screen are reversed. 00192 */ 00193 for (i=0; i < pi->map_width * pi->map_height; i++) { 00194 l = (uint8 *) (data + i*4); 00195 #if 1 00196 g = MAX(*l, *(l+1)); 00197 g = MAX(g, *(l+2)); 00198 #else 00199 g = ( *l + *(l+1) + *(l+2)) / 3; 00200 #endif 00201 p = (uint32*) fog->pixels + i; 00202 *p = (g << 8) | (g << 16) | (g << 24) | *(l + 3); 00203 } 00204 00205 for (i=0; i < pi->map_width * pi->map_height; i+= 4) { 00206 uint32 *tmp; 00207 00208 /* 00209 * The pointer arithemtic below looks suspicious, but it is a patch 00210 * that is submitted, so just putting it in as submitted. MSW 00211 * 2004-05-11 00212 */ 00213 p = (uint32*) (fog->pixels + i); 00214 g = ( ((*p >> 24) & 0xff) + ((*p >> 16) & 0xff) + ((*p >> 8) & 0xff)) / 3; 00215 tmp = (uint32*) fog->pixels + i; 00216 *tmp = (g << 24) | (g << 16) | (g << 8) | (*p & 0xff); 00217 } 00218 00219 SDL_UnlockSurface(fog); 00220 pi->fog_image = fog; 00221 #endif 00222 00223 #endif 00224 } 00225 else if (use_config[CONFIG_DISPLAYMODE] == CFG_DM_OPENGL){ 00226 #ifdef HAVE_OPENGL 00227 create_opengl_map_image(data, pi); 00228 #endif 00229 } 00230 00231 else if (use_config[CONFIG_DISPLAYMODE] == CFG_DM_PIXMAP){ 00232 rgba_to_gdkpixmap(window_root->window, data, pi->map_width, pi->map_height, 00233 (GdkPixmap**)&pi->map_image, (GdkBitmap**)&pi->map_mask, 00234 gtk_widget_get_colormap(window_root)); 00235 } 00236 } 00237 00243 static void free_pixmap(PixmapInfo *pi) 00244 { 00245 if (pi->icon_image) g_object_unref(pi->icon_image); 00246 if (pi->icon_mask) g_object_unref(pi->icon_mask); 00247 if (pi->map_mask) gdk_pixmap_unref(pi->map_mask); 00248 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00249 #ifdef HAVE_SDL 00250 if (pi->map_image) { 00251 SDL_FreeSurface(pi->map_image); 00252 free(((SDL_Surface*)pi->map_image)->pixels); 00253 SDL_FreeSurface(pi->fog_image); 00254 /* 00255 * Minor memory leak here - SDL_FreeSurface() frees the pixel 00256 * data _unless_ SDL_CreateRGBSurfaceFrom() was used to create 00257 * the surface. SDL_CreateRGBSurfaceFrom() is used to create 00258 * the map data, which is why we need the free there. The 00259 * reason this is a minor memory leak is because 00260 * SDL_CreateRGBSurfaceFrom() is used to create the question 00261 * mark image, and without this free, that data is not freed. 00262 * However, with this, client crashes after disconnecting from 00263 * server with double free. 00264 */ 00265 /* free(((SDL_Surface*)pi->fog_image)->pixels);*/ 00266 } 00267 #endif 00268 } 00269 else if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_OPENGL) { 00270 #ifdef HAVE_OPENGL 00271 opengl_free_pixmap(pi); 00272 #endif 00273 } 00274 else if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP) { 00275 if (pi->map_image) { 00276 gdk_pixmap_unref(pi->map_image); 00277 } 00278 } 00279 } 00280 00294 int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height) 00295 { 00296 int nx, ny, iscale, factor; 00297 uint8 *png_tmp; 00298 PixmapInfo *pi; 00299 00300 if (pixmap_num <= 0 || pixmap_num >= MAXPIXMAPNUM) 00301 return 1; 00302 00303 if (pixmaps[pixmap_num] != pixmaps[0]) { 00304 free_pixmap(pixmaps[pixmap_num]); 00305 free(pixmaps[pixmap_num]); 00306 pixmaps[pixmap_num] = pixmaps[0]; 00307 } 00308 00309 pi = calloc(1, sizeof(PixmapInfo)); 00310 00311 iscale = use_config[CONFIG_ICONSCALE]; 00312 00313 /* 00314 * If the image is big, figure out what we should scale it to so it fits 00315 * better display 00316 */ 00317 if (width > DEFAULT_IMAGE_SIZE || height>DEFAULT_IMAGE_SIZE) { 00318 int ts = 100; 00319 00320 factor = width / DEFAULT_IMAGE_SIZE; 00321 if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1; 00322 if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor]; 00323 00324 factor = height / DEFAULT_IMAGE_SIZE; 00325 if (factor >= MAX_ICON_SPACES) factor = MAX_ICON_SPACES - 1; 00326 if (icon_rescale_factor[factor] < ts) ts = icon_rescale_factor[factor]; 00327 00328 iscale = ts * use_config[CONFIG_ICONSCALE] / 100; 00329 } 00330 00331 /* In all cases, the icon images are in native form. */ 00332 if (iscale != 100) { 00333 nx=width; 00334 ny=height; 00335 png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, iscale); 00336 pi->icon_width = nx; 00337 pi->icon_height = ny; 00338 create_icon_image(png_tmp, pi, pixmap_num); 00339 free(png_tmp); 00340 } 00341 else { 00342 pi->icon_width = width; 00343 pi->icon_height = height; 00344 create_icon_image(rgba_data, pi, pixmap_num); 00345 } 00346 00347 /* 00348 * If icon_scale matched use_config[CONFIG_MAPSCALE], we could try to be 00349 * more intelligent, but this should not be called too often, and this 00350 * keeps the code simpler. 00351 */ 00352 if (use_config[CONFIG_MAPSCALE] != 100) { 00353 nx=width; 00354 ny=height; 00355 png_tmp = rescale_rgba_data(rgba_data, &nx, &ny, use_config[CONFIG_MAPSCALE]); 00356 pi->map_width = nx; 00357 pi->map_height = ny; 00358 create_map_image(png_tmp, pi); 00359 /* 00360 * pixmap mode and opengl don't need the rgba data after they have 00361 * created the image, so we can free it. SDL uses the raw rgba data, 00362 * so it can't be freed. 00363 */ 00364 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP || 00365 use_config[CONFIG_DISPLAYMODE]==CFG_DM_OPENGL) free(png_tmp); 00366 } else { 00367 pi->map_width = width; 00368 pi->map_height = height; 00369 /* 00370 * If using SDL mode, a copy of the rgba data needs to be stored away. 00371 */ 00372 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00373 png_tmp = malloc(width * height * BPP); 00374 memcpy(png_tmp, rgba_data, width * height * BPP); 00375 } else 00376 png_tmp = rgba_data; 00377 create_map_image(png_tmp, pi); 00378 } 00379 /* 00380 * Not ideal, but if it is missing the map or icon image, presume something 00381 * failed. However, opengl doesn't set the map_image, so if using that 00382 * display mode, don't make this check. 00383 */ 00384 if (!pi->icon_image || (!pi->map_image && use_config[CONFIG_DISPLAYMODE]!=CFG_DM_OPENGL)) { 00385 free_pixmap(pi); 00386 free(pi); 00387 return 1; 00388 } 00389 if (ce) { 00390 ce->image_data = pi; 00391 } 00392 pixmaps[pixmap_num] = pi; 00393 return 0; 00394 } 00395 00402 void addsmooth(uint16 face, uint16 smooth_face) 00403 { 00404 pixmaps[face]->smooth_face = smooth_face; 00405 } 00406 00415 int associate_cache_entry(Cache_Entry *ce, int pixnum) 00416 { 00417 pixmaps[pixnum] = ce->image_data; 00418 return 0; 00419 } 00420 00427 void reset_image_data(void) 00428 { 00429 int i; 00430 00431 reset_image_cache_data(); 00432 /* 00433 * The entries in the pixmaps array are also tracked in the image cache in 00434 * the common area. We will try to recycle those images that we can. 00435 * Thus, if we connect to a new server, we can just re-use the images we 00436 * have already rendered. 00437 */ 00438 for (i=1; i<MAXPIXMAPNUM; i++) { 00439 if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) { 00440 free_pixmap(pixmaps[i]); 00441 free(pixmaps[i]); 00442 pixmaps[i] = pixmaps[0]; 00443 } 00444 } 00445 } 00446 00447 static GtkWidget *pbar=NULL; 00448 static GtkWidget *pbar_window=NULL; 00449 static GtkAdjustment *padj=NULL; 00450 00462 void image_update_download_status(int start, int end, int total) 00463 { 00464 int x, y, wx, wy, w, h; 00465 00466 if (start == 1) { 00467 padj = (GtkAdjustment*) gtk_adjustment_new (0, 1, total, 0, 0, 0); 00468 00469 pbar = gtk_progress_bar_new_with_adjustment(padj); 00470 gtk_progress_set_format_string(GTK_PROGRESS(pbar), "Downloading image %v of %u (%p%% complete)"); 00471 gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(pbar), GTK_PROGRESS_CONTINUOUS); 00472 gtk_progress_set_show_text(GTK_PROGRESS(pbar), TRUE); 00473 get_window_coord(window_root, &x,&y, &wx,&wy,&w,&h); 00474 00475 pbar_window = gtk_window_new(GTK_WINDOW_POPUP); 00476 gtk_window_set_policy(GTK_WINDOW(pbar_window), TRUE, TRUE, FALSE); 00477 gtk_window_set_transient_for(GTK_WINDOW(pbar_window), GTK_WINDOW (window_root)); 00478 /* 00479 * We more or less want this window centered on the main crossfire 00480 * window, and not necessarily centered on the screen or in the upper 00481 * left corner. 00482 */ 00483 gtk_widget_set_uposition(pbar_window, (wx + w)/2, (wy + h) / 2); 00484 00485 gtk_container_add(GTK_CONTAINER(pbar_window), pbar); 00486 gtk_widget_show(pbar); 00487 gtk_widget_show(pbar_window); 00488 } 00489 if (start == total) { 00490 gtk_widget_destroy(pbar_window); 00491 pbar = NULL; 00492 pbar_window = NULL; 00493 padj = NULL; 00494 return; 00495 } 00496 00497 gtk_progress_set_value(GTK_PROGRESS(pbar), start); 00498 while ( gtk_events_pending() ) { 00499 gtk_main_iteration(); 00500 } 00501 } 00502 00509 void get_map_image_size(int face, uint8 *w, uint8 *h) 00510 { 00511 /* We want to calculate the number of spaces this image 00512 * uses it. By adding the image size but substracting one, 00513 * we cover the cases where the image size is not an even 00514 * increment. EG, if the map_image_size is 32, and an image 00515 * is 33 wide, we want that to register as two spaces. By 00516 * adding 31, that works out. 00517 */ 00518 if ( face < 0 || face >= MAXPIXMAPNUM) { 00519 *w = 1; 00520 *h = 1; 00521 } else { 00522 *w = (pixmaps[face]->map_width + map_image_size - 1)/ map_image_size; 00523 *h = (pixmaps[face]->map_height + map_image_size - 1)/ map_image_size; 00524 } 00525 } 00526 00527 /****************************************************************************** 00528 * 00529 * Code related to face caching. 00530 * 00531 *****************************************************************************/ 00532 00542 void init_cache_data(void) 00543 { 00544 int i; 00545 GtkStyle *style; 00546 #include "../../pixmaps/question.xpm" 00547 00548 00549 LOG(LOG_INFO,"gtk::init_cache_data","Init Cache"); 00550 00551 style = gtk_widget_get_style(window_root); 00552 pixmaps[0] = malloc(sizeof(PixmapInfo)); 00553 pixmaps[0]->icon_image = gdk_pixmap_create_from_xpm_d(window_root->window, 00554 (GdkBitmap**)&pixmaps[0]->icon_mask, 00555 &style->bg[GTK_STATE_NORMAL], 00556 (gchar **)question_xpm); 00557 #ifdef HAVE_SDL 00558 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL) { 00559 /* 00560 * Make a semi-transparent question mark symbol to use for the cached 00561 * images. 00562 */ 00563 #include "../../pixmaps/question.sdl" 00564 pixmaps[0]->map_image = SDL_CreateRGBSurfaceFrom(question_sdl, 00565 32, 32, 1, 4, 1, 1, 1, 1); 00566 SDL_SetAlpha(pixmaps[0]->map_image, SDL_SRCALPHA, 70); 00567 pixmaps[0]->fog_image = SDL_CreateRGBSurfaceFrom(question_sdl, 00568 32, 32, 1, 4, 1, 1, 1, 1); 00569 SDL_SetAlpha(pixmaps[0]->fog_image, SDL_SRCALPHA, 70); 00570 } 00571 else 00572 #endif 00573 if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_PIXMAP) 00574 { 00575 pixmaps[0]->map_image = pixmaps[0]->icon_image; 00576 pixmaps[0]->fog_image = pixmaps[0]->icon_image; 00577 pixmaps[0]->map_mask = pixmaps[0]->icon_mask; 00578 } 00579 #ifdef HAVE_OPENGL 00580 else if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_OPENGL) { 00581 create_opengl_question_mark(); 00582 } 00583 #endif 00584 00585 pixmaps[0]->icon_width = pixmaps[0]->icon_height = pixmaps[0]->map_width = pixmaps[0]->map_height = map_image_size; 00586 pixmaps[0]->smooth_face = 0; 00587 00588 /* Don't do anything special for SDL image - rather, that drawing 00589 * code will check to see if there is no data 00590 */ 00591 00592 /* Initialize all the images to be of the same value. */ 00593 for (i=1; i<MAXPIXMAPNUM; i++) { 00594 pixmaps[i] = pixmaps[0]; 00595 } 00596 00597 init_common_cache_data(); 00598 }