Index: client/civclient.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/civclient.c,v retrieving revision 1.155 diff -u -u -r1.155 civclient.c --- client/civclient.c 2002/11/28 19:09:57 1.155 +++ client/civclient.c 2002/12/14 22:41:31 @@ -676,6 +676,7 @@ role_unit_precalcs(); boot_help_texts(); /* reboot */ update_unit_focus(); + update_whole_tile_sprite_cache(); } else if(client_state==CLIENT_PRE_GAME_STATE) { popdown_all_city_dialogs(); Index: client/control.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/control.c,v retrieving revision 1.88 diff -u -u -r1.88 control.c --- client/control.c 2002/12/11 10:39:41 1.88 +++ client/control.c 2002/12/14 22:41:34 @@ -981,6 +981,7 @@ return; draw_terrain ^= 1; + update_whole_tile_sprite_cache(); update_map_canvas_visible(); } @@ -1005,6 +1006,7 @@ return; draw_roads_rails ^= 1; + update_whole_tile_sprite_cache(); update_map_canvas_visible(); } @@ -1017,6 +1019,7 @@ return; draw_irrigation ^= 1; + update_whole_tile_sprite_cache(); update_map_canvas_visible(); } @@ -1029,6 +1032,7 @@ return; draw_mines ^= 1; + update_whole_tile_sprite_cache(); update_map_canvas_visible(); } @@ -1041,6 +1045,7 @@ return; draw_fortress_airbase ^= 1; + update_whole_tile_sprite_cache(); update_map_canvas_visible(); } @@ -1053,6 +1058,7 @@ return; draw_specials ^= 1; + update_whole_tile_sprite_cache(); update_map_canvas_visible(); } @@ -1065,6 +1071,7 @@ return; draw_pollution ^= 1; + update_whole_tile_sprite_cache(); update_map_canvas_visible(); } @@ -1113,6 +1120,7 @@ return; draw_fog_of_war ^= 1; + update_whole_tile_sprite_cache(); update_map_canvas_visible(); refresh_overview_canvas(); refresh_overview_viewrect(); Index: client/packhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v retrieving revision 1.264 diff -u -u -r1.264 packhand.c --- client/packhand.c 2002/12/11 10:39:41 1.264 +++ client/packhand.c 2002/12/14 22:41:39 @@ -1048,6 +1048,7 @@ map_allocate(); climap_init_continents(); init_client_goto(); + init_tile_sprite_cache(); set_overview_dimensions(map.xsize, map.ysize); } @@ -1600,6 +1601,10 @@ /* refresh tiles */ if(get_client_state()==CLIENT_GAME_RUNNING_STATE) { int x = packet->x, y = packet->y; + + if (tile_changed || ptile->known != old_known) { + update_tile_sprite_cache_at_map_position(x, y); + } /* the tile itself */ if (tile_changed || old_known!=ptile->known) Index: client/tilespec.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v retrieving revision 1.94 diff -u -u -r1.94 tilespec.c --- client/tilespec.c 2002/12/13 19:15:11 1.94 +++ client/tilespec.c 2002/12/14 22:41:44 @@ -113,8 +113,158 @@ /* If no_backdrop is true, then no color/flag is drawn behind the city/unit. */ -static bool no_backdrop = FALSE; +static int no_backdrop = 0; +static int base_fill_tile_sprite_array(struct Sprite **sprs, int abs_x0, + int abs_y0); +static int get_darkness_sprite_index(int map_x,int map_y); + +#define TILE_SPRITE_CACHE_SIZE 1000 +/* Can currently be 21 */ +#define TILE_SPRITE_CACHE_MAX_COMPONENTS 25 +#define USE_TILE_SPRITE_CACHE 1 + +static struct { + int entries_used; + struct { + int refcount, components_used; + struct Sprite *result; + struct Sprite *components[TILE_SPRITE_CACHE_MAX_COMPONENTS]; + } entries[TILE_SPRITE_CACHE_SIZE]; +} tile_sprite_cache; + +void init_tile_sprite_cache(void) +{ + int i; + + tile_sprite_cache.entries_used = 0; + for (i = 0; i < TILE_SPRITE_CACHE_SIZE; i++) { + tile_sprite_cache.entries[i].refcount = 0; + } +} + +#if USE_TILE_SPRITE_CACHE +static struct Sprite *merge_sprite_array(struct Sprite **sprites, int count) +{ + int i; + struct Sprite *result = NULL; + + assert(count > 0); + result = clone_sprite(sprites[0]); + for (i = 1; i < count; i++) { + merge_sprites(result, sprites[i]); + } + + return result; +} + +static void tile_sprite_cache_update(int x, int y) +{ + struct tile *ptile = map_get_tile(x, y); + int index = ptile->client.tile_sprite_cache_index; + struct Sprite *sprites[80]; + int i, sprites_used, found, unused_entry; + + ptile->client.darkness_sprite_index = get_darkness_sprite_index(x, y); + + if (index != -1) { + assert(tile_sprite_cache.entries[index].refcount>0); + tile_sprite_cache.entries[index].refcount--; + if (tile_sprite_cache.entries[index].refcount == 0) { + tile_sprite_cache.entries_used--; + free_sprite(tile_sprite_cache.entries[index].result); + } + } + + sprites_used = base_fill_tile_sprite_array(sprites, x, y); + + found = 0; + unused_entry = -1; + for (i = 0; i < TILE_SPRITE_CACHE_SIZE; i++) { + if (tile_sprite_cache.entries[i].refcount == 0 && unused_entry == -1) { + unused_entry = i; + } + if (tile_sprite_cache.entries[i].refcount > 0 + && tile_sprite_cache.entries[i].components_used == sprites_used && + memcmp(sprites, tile_sprite_cache.entries[i].components, + sizeof(struct Sprite *) * sprites_used) == 0) { + found = 1; + break; + } + } + + if (found) { + index = i; + freelog(LOG_DEBUG, + "hit for (%d,%d) entry: index=%d refcount=%d sprites=%d", x, y, + index, tile_sprite_cache.entries[index].refcount, + sprites_used); + } else { + tile_sprite_cache.entries_used++; + assert(unused_entry != -1); + index = unused_entry; + tile_sprite_cache.entries[index].refcount = 0; + tile_sprite_cache.entries[index].components_used = sprites_used; + memcpy(tile_sprite_cache.entries[index].components, sprites, + sizeof(struct Sprite *) * sprites_used); + if (sprites_used > 0) { + tile_sprite_cache.entries[index].result = + merge_sprite_array(tile_sprite_cache.entries[index].components, + sprites_used); + } + freelog(LOG_DEBUG, + "miss for (%d,%d) entry: index=%d refcount=%d sprites=%d", x, y, + index, tile_sprite_cache.entries[index].refcount, + sprites_used); + } + + tile_sprite_cache.entries[index].refcount++; + ptile->client.tile_sprite_cache_index = index; +} +#endif + +void update_tile_sprite_cache_at_map_position(int x, int y) +{ +#if USE_TILE_SPRITE_CACHE + tile_sprite_cache_update(x, y); + adjc_iterate(x, y, x1, y1) { + tile_sprite_cache_update(x1, y1); + } adjc_iterate_end; +#endif +} + +void update_whole_tile_sprite_cache(void) +{ +#if USE_TILE_SPRITE_CACHE + freelog(LOG_NORMAL,"Filling tile sprite cache..."); + whole_map_iterate(x, y) { + tile_sprite_cache_update(x,y); + } whole_map_iterate_end; + freelog(LOG_NORMAL, "...done. Cache contains %d entries", + tile_sprite_cache.entries_used); + + { + int i; + int count[80]; + + memset(count, 0, sizeof(count)); + + for (i = 0; i < TILE_SPRITE_CACHE_SIZE; i++) { + if (tile_sprite_cache.entries[i].refcount > 0) { + count[tile_sprite_cache.entries[i].components_used]++; + } + } + + for (i = 1; i < 80; i++) { + if (count[i] > 0) { + freelog(LOG_NORMAL, " %d times were %d sprites merged", + count[i], i); + } + } + } +#endif +} + /********************************************************************** Returns a static list of tilesets available on the system by searching all data directories for files matching TILESPEC_SUFFIX. @@ -1284,6 +1434,7 @@ int x, int y, bool citymode, int *solid_bg) { +#if 0 int ttype, ttype_near[8]; int tspecial, tspecial_near[8]; int tileno, dir, i; @@ -1485,6 +1636,7 @@ * given the same marking as our current tile - that way we won't * get the "unknown" dither along the edge of the map. */ +#if 0 for (dir = 0; dir < 4; dir++) { int x1, y1, other; @@ -1494,8 +1646,32 @@ other = ttype_near[dir]; dither[dir] = get_dither(ttype, other); } - return sprs - save_sprs; +#endif +#endif + return 0; +} + +static int get_darkness_sprite_index(int map_x,int map_y) +{ +#if 0 + /* + * We're looking to find the INDEX_NSEW for the directions that + * are unknown + */ + int known[4]; + memset(known, 0, sizeof(known)); + adjc_dir_iterate(map_x, map_y, x, y, dir8) { + if (!DIR_IS_CARDINAL(dir8)) + continue; + known[dir8_to_dir4(dir8)] = (tile_is_known(x, y) != TILE_UNKNOWN); + } adjc_dir_iterate_end; + + return INDEX_NSEW(!known[DIR4_NORTH], !known[DIR4_SOUTH], + !known[DIR4_EAST], !known[DIR4_WEST]); +#else + return 0; +#endif } /********************************************************************** @@ -1515,8 +1691,8 @@ 11) fallout 12) FoW ***********************************************************************/ -int fill_tile_sprite_array(struct Sprite **sprs, int abs_x0, int abs_y0, - bool citymode, int *solid_bg, struct player **pplayer) +static int base_fill_tile_sprite_array(struct Sprite **sprs, int abs_x0, + int abs_y0) { int ttype, ttype_near[8]; int tspecial, tspecial_near[8]; @@ -1527,14 +1703,9 @@ int tileno; struct tile *ptile; struct Sprite *mysprite; - struct city *pcity; - struct unit *pfocus; - struct unit *punit; int den_y=map.ysize*.24; struct Sprite **save_sprs=sprs; - *solid_bg = 0; - *pplayer = NULL; ptile=map_get_tile(abs_x0, abs_y0); @@ -1542,31 +1713,22 @@ return 0; } - pcity=map_get_city(abs_x0, abs_y0); - pfocus=get_unit_in_focus(); - - if(!flags_are_transparent || solid_color_behind_units) { - /* non-transparent flags -> just draw city or unit */ - - punit = get_drawable_unit(abs_x0, abs_y0, citymode); - if (punit && (draw_units || (draw_focus_unit && pfocus == punit))) { - sprs += fill_unit_sprite_array(sprs, punit, solid_bg); - *pplayer = unit_owner(punit); - if (unit_list_size(&ptile->units) > 1) - *sprs++ = sprites.unit.stack; - return sprs - save_sprs; - } + tspecial = map_get_special(abs_x0, abs_y0); + ttype = map_get_terrain(abs_x0, abs_y0); - if (pcity && draw_cities) { - sprs+=fill_city_sprite_array(sprs, pcity, solid_bg); - *pplayer = city_owner(pcity); - return sprs - save_sprs; + /* Any unreal tile have no specials and the terrain type of (x, y). */ + for (dir = 0; dir < 8; dir++) { + int x, y; + + if (MAPSTEP(x, y, abs_x0, abs_y0, dir)) { + tspecial_near[dir] = map_get_special(x, y); + ttype_near[dir] = map_get_terrain(x, y); + } else { + tspecial_near[dir] = S_NO_SPECIAL; + ttype_near[dir] = ttype; } } - tspecial = map_get_special(abs_x0, abs_y0); - ttype = map_get_terrain(abs_x0, abs_y0); - /* Any unreal tile have no specials and the terrain type of (x, y). */ for (dir = 0; dir < 8; dir++) { int x, y; @@ -1599,8 +1761,10 @@ if (draw_terrain) *sprs++=mysprite; +#if 0 else *solid_bg = 1; +#endif if(ttype==T_OCEAN && draw_terrain) { int dir; @@ -1748,15 +1912,76 @@ } } - if(contains_special(tspecial, S_HUT) && draw_specials) *sprs++ = sprites.tx.village; - if(contains_special(tspecial, S_FORTRESS) && draw_fortress_airbase) *sprs++ = sprites.tx.fortress; - if(contains_special(tspecial, S_AIRBASE) && draw_fortress_airbase) *sprs++ = sprites.tx.airbase; - if(contains_special(tspecial, S_POLLUTION) && draw_pollution) *sprs++ = sprites.tx.pollution; - if(contains_special(tspecial, S_FALLOUT) && draw_pollution) *sprs++ = sprites.tx.fallout; - if(tile_get_known(abs_x0,abs_y0) == TILE_KNOWN_FOGGED && draw_fog_of_war) - *sprs++ = sprites.tx.fog; + if(tspecial & S_HUT && draw_specials) *sprs++ = sprites.tx.village; + if(tspecial & S_FORTRESS && draw_fortress_airbase) *sprs++ = sprites.tx.fortress; + if(tspecial & S_AIRBASE && draw_fortress_airbase) *sprs++ = sprites.tx.airbase; + if(tspecial & S_POLLUTION && draw_pollution) *sprs++ = sprites.tx.pollution; + if(tspecial & S_FALLOUT && draw_pollution) *sprs++ = sprites.tx.fallout; + if(ptile->known==TILE_KNOWN_FOGGED && draw_fog_of_war) *sprs++ = sprites.tx.fog; + + return sprs - save_sprs; +} + +int fill_tile_sprite_array(struct Sprite **sprs, int abs_x0, int abs_y0, + bool citymode, int *solid_bg, + struct player **pplayer) +{ + int tileno; + struct tile *ptile; + struct city *pcity; + struct unit *pfocus; + struct unit *punit; + + struct Sprite **save_sprs=sprs; + *solid_bg = 0; + *pplayer = NULL; + + assert(is_normal_map_pos(abs_x0, abs_y0)); + ptile=map_get_tile(abs_x0, abs_y0); + + if (ptile->known == TILE_UNKNOWN) { + return 0; + } + + pcity=map_get_city(abs_x0, abs_y0); + pfocus=get_unit_in_focus(); + + if(!flags_are_transparent || solid_color_behind_units) { + /* non-transparent flags -> just draw city or unit */ + + punit = get_drawable_unit(abs_x0, abs_y0, citymode); + if (punit && (draw_units || (draw_focus_unit && pfocus == punit))) { + sprs += fill_unit_sprite_array(sprs, punit, solid_bg); + *pplayer = unit_owner(punit); + if (unit_list_size(&ptile->units) > 1) + *sprs++ = sprites.unit.stack; + return sprs - save_sprs; + } + if (pcity && draw_cities) { + sprs+=fill_city_sprite_array(sprs, pcity, solid_bg); + *pplayer = city_owner(pcity); + return sprs - save_sprs; + } + } + +#if USE_TILE_SPRITE_CACHE + assert(ptile->client.tile_sprite_cache_index != -1); + if (tile_sprite_cache.entries[ptile->client.tile_sprite_cache_index]. + components_used > 0) { + *sprs++ = + tile_sprite_cache.entries[ptile->client. + tile_sprite_cache_index].result; + } + tileno = ptile->client.darkness_sprite_index; +#else + sprs += base_fill_tile_sprite_array(sprs, abs_x0, abs_y0); if (!citymode) { + tileno = get_darkness_sprite_index(abs_x0, abs_y0); + } +#endif + + if (!citymode) { /* * We're looking to find the INDEX_NSEW for the directions that * are unknown. We want to mark unknown tiles so that an unreal @@ -1765,6 +1990,7 @@ * map. */ bool known[4]; + int dir; for (dir = 0; dir < 4; dir++) { int x1, y1; Index: client/tilespec.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v retrieving revision 1.34 diff -u -u -r1.34 tilespec.h --- client/tilespec.h 2002/12/13 19:15:11 1.34 +++ client/tilespec.h 2002/12/14 22:41:44 @@ -63,6 +63,9 @@ void set_focus_unit_hidden_state(bool hide); struct unit *get_drawable_unit(int x, int y, bool citymode); +void init_tile_sprite_cache(void); +void update_tile_sprite_cache_at_map_position(int x,int y); +void update_whole_tile_sprite_cache(void); /* This the way directional indices are now encoded: */ Index: client/gui-gtk/graphics.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/graphics.c,v retrieving revision 1.47 diff -u -u -r1.47 graphics.c --- client/gui-gtk/graphics.c 2002/11/14 09:14:53 1.47 +++ client/gui-gtk/graphics.c 2002/12/14 22:41:46 @@ -15,6 +15,7 @@ #include #endif +#include #include #include #include @@ -610,6 +611,37 @@ } gdk_image_destroy(mask_image); +} + +void merge_sprites(SPRITE * sprite, const SPRITE * const other) +{ + GdkColor pixel; + GdkGC *mask_fg_gc = gdk_gc_new(sprite->mask); + + pixel.pixel = 1; + gdk_gc_set_foreground(mask_fg_gc, &pixel); + + assert(sprite); + assert(other); + + gdk_gc_set_clip_origin(civ_gc, 0, 0); + gdk_gc_set_clip_origin(mask_fg_gc, 0, 0); + gdk_gc_set_clip_mask(civ_gc, other->mask); + gdk_gc_set_clip_mask(mask_fg_gc, other->mask); + + gdk_draw_pixmap(sprite->pixmap, civ_gc, other->pixmap, + 0, 0, 0, 0, other->width, other->height); + + gdk_draw_pixmap(sprite->mask, mask_fg_gc, other->mask, + 0, 0, 0, 0, other->width, other->height); + gdk_gc_set_clip_mask(civ_gc, NULL); + gdk_gc_set_clip_mask(mask_fg_gc, NULL); + gdk_gc_destroy(mask_fg_gc); +} + +struct Sprite *clone_sprite(struct Sprite *source) +{ + return crop_sprite(source, 0, 0, source->width, source->height); } /********************************************************************* Index: client/gui-gtk/mapview.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/mapview.c,v retrieving revision 1.144 diff -u -u -r1.144 mapview.c --- client/gui-gtk/mapview.c 2002/12/13 19:15:11 1.144 +++ client/gui-gtk/mapview.c 2002/12/14 22:41:50 @@ -1050,6 +1050,10 @@ "update_map_canvas(pos=(%d,%d), size=(%d,%d), write_to_screen=%d)", x, y, width, height, write_to_screen); + if (get_client_state() != CLIENT_GAME_RUNNING_STATE) { + return; + } + if (is_isometric) { int i; int x_itr, y_itr; @@ -1309,6 +1313,10 @@ **************************************************************************/ void put_nuke_mushroom_pixmaps(int x, int y) { + if (get_client_state() != CLIENT_GAME_RUNNING_STATE) { + return; + } + if (is_isometric) { int canvas_x, canvas_y; struct Sprite *mysprite = sprites.explode.iso_nuke; Index: client/include/graphics_g.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/client/include/graphics_g.h,v retrieving revision 1.8 diff -u -u -r1.8 graphics_g.h --- client/include/graphics_g.h 2002/11/07 16:04:53 1.8 +++ client/include/graphics_g.h 2002/12/14 22:41:51 @@ -30,6 +30,11 @@ struct Sprite *load_gfxfile(const char *filename); struct Sprite *crop_sprite(struct Sprite *source, int x, int y, int width, int height); + +struct Sprite *clone_sprite(struct Sprite *s); void free_sprite(struct Sprite *s); + +void merge_sprites(struct Sprite *this_sprite, + const struct Sprite *const other); #endif /* FC__GRAPHICS_G_H */ Index: common/map.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/map.c,v retrieving revision 1.130 diff -u -u -r1.130 map.c --- common/map.c 2002/11/14 09:15:02 1.130 +++ common/map.c 2002/12/14 22:41:54 @@ -1147,6 +1147,7 @@ unit_list_init(&ptile->units); ptile->worked = NULL; /* pointer to city working tile */ ptile->assigned = 0; /* bitvector */ + ptile->client.tile_sprite_cache_index = -1; } /*************************************************************** Index: common/map.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/map.h,v retrieving revision 1.132 diff -u -u -r1.132 map.h --- common/map.h 2002/11/03 23:22:44 1.132 +++ common/map.h 2002/12/14 22:41:56 @@ -62,6 +62,9 @@ struct city *worked; /* city working tile, or NULL if none */ signed short continent; signed char move_cost[8]; /* don't know if this helps! */ + struct { + int tile_sprite_cache_index, darkness_sprite_index; + } client; };