diff -urd -X freeciv.current/diff_ignore freeciv.current/client/gui-gtk/graphics.c work/client/gui-gtk/graphics.c --- freeciv.current/client/gui-gtk/graphics.c Sun Feb 4 14:45:19 2001 +++ work/client/gui-gtk/graphics.c Fri Sep 7 19:43:21 2001 @@ -58,9 +58,6 @@ GdkCursor * nuke_cursor; GdkCursor * patrol_cursor; -static SPRITE *ctor_sprite_mask(GdkPixmap *mypixmap, GdkPixmap *mask, - int width, int height); - /*************************************************************************** ... ***************************************************************************/ @@ -392,4 +389,221 @@ free_sprite(radar_gfx_sprite); radar_gfx_sprite=NULL; } +} + +/*************************************************************************** + Draws a black border around the sprite. This is done by parsing the + mask and replacing the first and last pixel in every column and row + by a black one. +***************************************************************************/ +void sprite_draw_black_border(SPRITE * sprite) +{ + GdkImage *mask_image, *pixmap_image; + int x, y; + + pixmap_image = + gdk_image_get(sprite->pixmap, 0, 0, sprite->width, sprite->height); + + mask_image = + gdk_image_get(sprite->mask, 0, 0, sprite->width, sprite->height); + + /* parsing columns */ + for (x = 0; x < sprite->width; x++) { + for (y = 0; y < sprite->height; y++) { + if (gdk_image_get_pixel(mask_image, x, y) != 0) { + gdk_image_put_pixel(pixmap_image, x, y, 0); + break; + } + } + + for (y = sprite->height - 1; y > 0; y--) { + if (gdk_image_get_pixel(mask_image, x, y) != 0) { + gdk_image_put_pixel(pixmap_image, x, y, 0); + break; + } + } + } + + /* parsing rows */ + for (y = 0; y < sprite->height; y++) { + for (x = 0; x < sprite->width; x++) { + if (gdk_image_get_pixel(mask_image, x, y) != 0) { + gdk_image_put_pixel(pixmap_image, x, y, 0); + break; + } + } + + for (x = sprite->width - 1; x > 0; x--) { + if (gdk_image_get_pixel(mask_image, x, y) != 0) { + gdk_image_put_pixel(pixmap_image, x, y, 0); + break; + } + } + } + + gdk_draw_image(sprite->pixmap, civ_gc, pixmap_image, 0, 0, 0, 0, + sprite->width, sprite->height); + + gdk_image_destroy(mask_image); + gdk_image_destroy(pixmap_image); +} + +/*************************************************************************** + Scales a sprite. If the sprite contains a mask, the mask is scaled + as as well. The algorithm produces rather fast but beautiful + results. +***************************************************************************/ +SPRITE* sprite_scale(SPRITE *src, int new_w, int new_h) +{ + SPRITE *dst = ctor_sprite_mask(src->pixmap, src->mask, new_w, new_h); + GdkImage *xi_src, *xi_dst, *xb_src; + guint32 pixel; + int xoffset_table[4096]; + int x, xoffset, xadd, xremsum, xremadd; + int y, yoffset, yadd, yremsum, yremadd; + + xi_src = gdk_image_get(src->pixmap, 0, 0, src->width, src->height); + + xi_dst = gdk_image_new(GDK_IMAGE_FASTEST, + gdk_window_get_visual(root_window), new_w, new_h); + + /* for each pixel in dst, calculate pixel offset in src */ + xadd = src->width / new_w; + xremadd = src->width % new_w; + xoffset = 0; + xremsum = new_w / 2; + + for (x = 0; x < new_w; ++x) { + xoffset_table[x] = xoffset; + xoffset += xadd; + xremsum += xremadd; + if (xremsum >= new_w) { + xremsum -= new_w; + ++xoffset; + } + } + + yadd = src->height / new_h; + yremadd = src->height % new_h; + yoffset = 0; + yremsum = new_h / 2; + + if (src->has_mask) { + xb_src = gdk_image_get(src->mask, 0, 0, src->width, src->height); + + dst->mask = gdk_pixmap_new(root_window, new_w, new_h, 1); + gdk_draw_rectangle(dst->mask, mask_bg_gc, TRUE, 0, 0, -1, -1); + + for (y = 0; y < new_h; ++y) { + for (x = 0; x < new_w; ++x) { + pixel = gdk_image_get_pixel(xi_src, xoffset_table[x], yoffset); + gdk_image_put_pixel(xi_dst, x, y, pixel); + + if (gdk_image_get_pixel(xb_src, xoffset_table[x], yoffset) != 0) { + gdk_draw_point(dst->mask, mask_fg_gc, x, y); + } + } + + yoffset += yadd; + yremsum += yremadd; + if (yremsum >= new_h) { + yremsum -= new_h; + ++yoffset; + } + } + + gdk_image_destroy(xb_src); + } else { + for (y = 0; y < new_h; ++y) { + for (x = 0; x < new_w; ++x) { + pixel = gdk_image_get_pixel(xi_src, xoffset_table[x], yoffset); + gdk_image_put_pixel(xi_dst, x, y, pixel); + } + + yoffset += yadd; + yremsum += yremadd; + if (yremsum >= new_h) { + yremsum -= new_h; + ++yoffset; + } + } + } + + dst->pixmap = gdk_pixmap_new(root_window, new_w, new_h, -1); + gdk_draw_image(dst->pixmap, civ_gc, xi_dst, 0, 0, 0, 0, new_w, new_h); + gdk_image_destroy(xi_src); + gdk_image_destroy(xi_dst); + + dst->has_mask = src->has_mask; + return dst; +} + +/*************************************************************************** + Method returns the bounding box of a sprite. It assumes a rectangular + object/mask. The bounding box contains the border (pixel which have + unset pixel as neighbours) pixel. +***************************************************************************/ +void sprite_get_bounding_box(SPRITE * sprite, int *start_x, + int *start_y, int *end_x, int *end_y) +{ + GdkImage *mask_image; + int i, j; + + if (!sprite->has_mask || sprite->mask == NULL) { + *start_x = 0; + *start_y = 0; + *end_x = sprite->width - 1; + *end_y = sprite->height - 1; + return; + } + + mask_image = + gdk_image_get(sprite->mask, 0, 0, sprite->width, sprite->height); + + + /* parses mask image for the first column that contains a visible pixel */ + *start_x = -1; + for (i = 0; i < sprite->width && *start_x == -1; i++) { + for (j = 0; j < sprite->height; j++) { + if (gdk_image_get_pixel(mask_image, i, j) != 0) { + *start_x = i; + break; + } + } + } + + /* parses mask image for the last column that contains a visible pixel */ + *end_x = -1; + for (i = sprite->width - 1; i >= *start_x && *end_x == -1; i--) { + for (j = 0; j < sprite->height; j++) { + if (gdk_image_get_pixel(mask_image, i, j) != 0) { + *end_x = i; + break; + } + } + } + + /* parses mask image for the first row that contains a visible pixel */ + *start_y = -1; + for (i = 0; i < sprite->height && *start_y == -1; i++) { + for (j = *start_x; j <= *end_x; j++) { + if (gdk_image_get_pixel(mask_image, j, i) != 0) { + *start_y = i; + break; + } + } + } + + /* parses mask image for the last row that contains a visible pixel */ + *end_y = -1; + for (i = sprite->height - 1; i >= *end_y && *end_y == -1; i--) { + for (j = *start_x; j <= *end_x; j++) { + if (gdk_image_get_pixel(mask_image, j, i) != 0) { + *end_y = i; + break; + } + } + } + + gdk_image_destroy(mask_image); } diff -urd -X freeciv.current/diff_ignore freeciv.current/client/gui-gtk/graphics.h work/client/gui-gtk/graphics.h --- freeciv.current/client/gui-gtk/graphics.h Mon Jan 29 19:55:27 2001 +++ work/client/gui-gtk/graphics.h Fri Sep 7 20:05:41 2001 @@ -37,4 +37,11 @@ extern GdkCursor * nuke_cursor; extern GdkCursor * patrol_cursor; +SPRITE *ctor_sprite_mask(GdkPixmap *mypixmap, GdkPixmap *mask, + int width, int height); +void sprite_draw_black_border(SPRITE * sprite); +SPRITE* sprite_scale(SPRITE *src, int new_w, int new_h); +void sprite_get_bounding_box(SPRITE * sprite, int *start_x, + int *start_y, int *end_x, int *end_y); + #endif /* FC__GRAPHICS_H */ diff -urd -X freeciv.current/diff_ignore freeciv.current/client/gui-gtk/plrdlg.c work/client/gui-gtk/plrdlg.c --- freeciv.current/client/gui-gtk/plrdlg.c Thu Aug 30 15:32:28 2001 +++ work/client/gui-gtk/plrdlg.c Fri Sep 7 20:06:55 2001 @@ -59,7 +59,7 @@ /* a simple macro that makes an often used construct look readable */ #define LI_2_PI(no) *(int*)(gtk_clist_get_row_data(GTK_CLIST(players_list), no)) -static GdkPixmap *flags[MAX_NUM_PLAYERS]; +static SPRITE *flags[MAX_NUM_PLAYERS]; static void create_players_dialog(void); static void players_button_callback(GtkWidget *w, gpointer data); @@ -337,53 +337,7 @@ row[10] = idlebuf; } -/* - * Method returns the bounding box of a sprite. It assumes a - * rectangular object/mask. The bounding box contains the border - * (pixel which have unset pixel as neighbours) pixel. - */ -static void get_sprite_bounding_box(SPRITE * sprite, int *start_x, - int *start_y, int *end_x, int *end_y) -{ - GdkImage *mask_image; - int i; - - if (!sprite->has_mask || sprite->mask == NULL) { - *start_x = 0; - *start_y = 0; - *end_x = sprite->width - 1; - *end_y = sprite->height - 1; - return; - } - - mask_image = - gdk_image_get(sprite->mask, 0, 0, sprite->width, sprite->height); - - for (i = 0; i < sprite->width; i++) - if (gdk_image_get_pixel(mask_image, i, 0) != 0) - break; - *start_x = i; - - for (i = 0; i < sprite->height; i++) - if (gdk_image_get_pixel(mask_image, 0, i) != 0) - break; - *start_y = i; - - for (i = sprite->width - 1; i >= 0; i--) - if (gdk_image_get_pixel(mask_image, i, 0) != 0) - break; - *end_x = i; - - for (i = sprite->height - 1; i >= 0; i--) - if (gdk_image_get_pixel(mask_image, 0, i) != 0) - break; - *end_y = i; - - gdk_image_destroy(mask_image); -} - -#define MINIMAL_FLAG_WIDTH 5 -#define MINIMAL_FLAG_HEIGHT 5 +#define MIN_DIMENSION 5 /* * Builds the flag pixmap. @@ -391,39 +345,44 @@ static void build_flag(int playerindex) { int start_x, start_y, end_x, end_y, flag_h, flag_w, newflag_h, newflag_w; - GdkPixmap *flag_pixmap; - SPRITE *flag_sprite; + SPRITE *flag, *croped, *scaled; - flag_sprite = get_nation_by_plr(&game.players[playerindex])->flag_sprite; + flag = get_nation_by_plr(&game.players[playerindex])->flag_sprite; - get_sprite_bounding_box(flag_sprite, &start_x, &start_y, &end_x, &end_y); + /* calculate the bounding box ... */ + sprite_get_bounding_box(flag, &start_x, &start_y, &end_x, &end_y); - assert(start_x == 0); - assert(start_y == 0); - assert(end_x >= MINIMAL_FLAG_WIDTH); - assert(end_y >= MINIMAL_FLAG_HEIGHT); + assert(start_x != -1); + assert(start_y != -1); + assert(end_x != -1); + assert(end_y != -1); flag_w = (end_x - start_x) + 1; flag_h = (end_y - start_y) + 1; - /* now scaling the original pixmap */ + /* if the flag is smaller then 5 x 5, something is wrong */ + assert(flag_w >= MIN_DIMENSION && flag_h >= MIN_DIMENSION); + + /* croping */ + croped = crop_sprite(flag, start_x, start_y, flag_w, flag_h); + + /* scaling */ newflag_h = GTK_CLIST(players_list)->row_height; newflag_w = ((double) newflag_h / flag_h) * flag_w; - freelog(LOG_DEBUG, "%dx%d %dx%d %dx%d", flag_sprite->width, - flag_sprite->height, flag_w, flag_h, newflag_w, newflag_h); + freelog(LOG_DEBUG, "%dx%d %dx%d %dx%d", flag->width, + flag->height, flag_w, flag_h, newflag_w, newflag_h); - flag_pixmap = gtk_scale_pixmap(flag_sprite->pixmap, flag_w, flag_h, - newflag_w, newflag_h); + scaled = sprite_scale(croped, newflag_w, newflag_h); + free_sprite(croped); - /* put a black border around it */ - gdk_gc_set_foreground(civ_gc, colors_standard[COLOR_STD_BLACK]); - gdk_draw_rectangle(flag_pixmap, civ_gc, 0, 0, 0, newflag_w, newflag_h); + sprite_draw_black_border(scaled); - /* and finaly store the pixmap in the static flags array */ - flags[playerindex] = flag_pixmap; + /* and finaly store the scaled flagsprite in the static flags array */ + flags[playerindex] = scaled; } + /************************************************************************** ... **************************************************************************/ @@ -454,8 +413,8 @@ &(listindex_to_playerindex[row])); build_flag(i); - gtk_clist_set_pixmap(GTK_CLIST(players_list), row, 1, flags[i], - NULL); + gtk_clist_set_pixmap(GTK_CLIST(players_list), row, 1, + flags[i]->pixmap, flags[i]->mask); listindex_to_playerindex[row] = i; playerindex_to_listindex[i] = row;