Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2005:
[Freeciv-Dev] Re: (PR#12557) RFC: new files sprite.c and canvas.c
Home

[Freeciv-Dev] Re: (PR#12557) RFC: new files sprite.c and canvas.c

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] Re: (PR#12557) RFC: new files sprite.c and canvas.c
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Mon, 21 Mar 2005 16:05:48 -0800
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=12557 >

Jason Short wrote:

> This patch makes the base change.
> 
> * New files canvas_g.h and sprite_g.h.
> * Some slightly changed includes.
> * New gui-stub files canvas.[ch] and sprite.[ch].

And here is a patch for gtk2.  Naturally it requires the other patch.

I cheated a bit by adding the new headers as includes inside the old 
headers (mapview and graphics) rather than adding new includes to all 
the .c files.  However I did remove the include from gui_main.h.  I also 
had to change gtkpixcomm.h to prevent a recursive include.

This patch is copy-and-paste but I fixed up a lot of comments.  Really 
the main advantage is that mapview.c (which is a monster) gets a little 
smaller.  However in future I think we could hide the structs and have 
accessors for everything.

-jason

Index: client/gui-gtk-2.0/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/Makefile.am,v
retrieving revision 1.7
diff -u -r1.7 Makefile.am
--- client/gui-gtk-2.0/Makefile.am      18 Oct 2004 23:49:27 -0000      1.7
+++ client/gui-gtk-2.0/Makefile.am      22 Mar 2005 00:01:58 -0000
@@ -22,6 +22,8 @@
 libguiclient_a_SOURCES = \
        rc2c            \
        Freeciv.h       \
+       canvas.c        \
+       canvas.h        \
        chatline.h      \
        chatline.c      \
        citydlg.c       \
@@ -81,6 +83,8 @@
        resources.h     \
        spaceshipdlg.c  \
        spaceshipdlg.h  \
+       sprite.c        \
+       sprite.h        \
        wldlg.c         \
        wldlg.h 
 
Index: client/gui-gtk-2.0/canvas.c
===================================================================
RCS file: client/gui-gtk-2.0/canvas.c
diff -N client/gui-gtk-2.0/canvas.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ client/gui-gtk-2.0/canvas.c 22 Mar 2005 00:01:58 -0000
@@ -0,0 +1,322 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "canvas.h"
+#include "colors.h"
+#include "gui_main.h"
+#include "mapview.h"
+
+/****************************************************************************
+  Create a canvas of the given size.
+****************************************************************************/
+struct canvas *canvas_create(int width, int height)
+{
+  struct canvas *result = fc_malloc(sizeof(*result));
+
+  result->type = CANVAS_PIXMAP;
+  result->v.pixmap = gdk_pixmap_new(root_window, width, height, -1);
+  return result;
+}
+
+/****************************************************************************
+  Free any resources associated with this canvas and the canvas struct
+  itself.
+****************************************************************************/
+void canvas_free(struct canvas *store)
+{
+  if (store->type == CANVAS_PIXMAP) {
+    g_object_unref(store->v.pixmap);
+  }
+  free(store);
+}
+
+/****************************************************************************
+  Copies an area from the source canvas to the destination canvas.
+****************************************************************************/
+void canvas_copy(struct canvas *dest, struct canvas *src,
+                int src_x, int src_y, int dest_x, int dest_y,
+                int width, int height)
+{
+  if (dest->type == src->type) {
+    if (src->type == CANVAS_PIXMAP) {
+      gdk_draw_drawable(dest->v.pixmap, fill_bg_gc, src->v.pixmap,
+                       src_x, src_y, dest_x, dest_y, width, height);
+    }
+  }
+}
+
+/****************************************************************************
+  Place part of a (possibly masked) sprite on a pixmap.
+****************************************************************************/
+static void pixmap_put_sprite(GdkDrawable *pixmap,
+                             int pixmap_x, int pixmap_y,
+                             struct Sprite *ssprite,
+                             int offset_x, int offset_y,
+                             int width, int height)
+{
+  if (ssprite->pixmap) {
+    if (ssprite->mask) {
+      gdk_gc_set_clip_origin(civ_gc, pixmap_x, pixmap_y);
+      gdk_gc_set_clip_mask(civ_gc, ssprite->mask);
+    }
+
+    gdk_draw_drawable(pixmap, civ_gc, ssprite->pixmap,
+                     offset_x, offset_y,
+                     pixmap_x + offset_x, pixmap_y + offset_y,
+                     MIN(width, MAX(0, ssprite->width - offset_x)),
+                     MIN(height, MAX(0, ssprite->height - offset_y)));
+
+    gdk_gc_set_clip_mask(civ_gc, NULL);
+  } else {
+    gdk_draw_pixbuf(pixmap, civ_gc, ssprite->pixbuf,
+                   offset_x, offset_y,
+                   pixmap_x + offset_x, pixmap_y + offset_y,
+                   MIN(width, MAX(0, ssprite->width - offset_x)),
+                   MIN(height, MAX(0, ssprite->height - offset_y)),
+                   GDK_RGB_DITHER_NONE, 0, 0);
+  }
+}
+
+/****************************************************************************
+  Draw some or all of a sprite onto the mapview or citydialog canvas.
+****************************************************************************/
+void canvas_put_sprite(struct canvas *pcanvas,
+                      int canvas_x, int canvas_y,
+                      struct Sprite *sprite,
+                      int offset_x, int offset_y, int width, int height)
+{
+  switch (pcanvas->type) {
+    case CANVAS_PIXMAP:
+      pixmap_put_sprite(pcanvas->v.pixmap, canvas_x, canvas_y,
+         sprite, offset_x, offset_y, width, height);
+      break;
+    case CANVAS_PIXCOMM:
+      gtk_pixcomm_copyto(pcanvas->v.pixcomm, sprite, canvas_x, canvas_y);
+      break;
+    case CANVAS_PIXBUF:
+      {
+       GdkPixbuf *src, *dst;
+
+       /* FIXME: is this right??? */
+       if (canvas_x < 0) {
+         offset_x -= canvas_x;
+         canvas_x = 0;
+       }
+       if (canvas_y < 0) {
+         offset_y -= canvas_y;
+         canvas_y = 0;
+       }
+
+
+       src = sprite_get_pixbuf(sprite);
+       dst = pcanvas->v.pixbuf;
+       gdk_pixbuf_composite(src, dst, canvas_x, canvas_y,
+           MIN(width,
+             MIN(gdk_pixbuf_get_width(dst), gdk_pixbuf_get_width(src))),
+           MIN(height,
+             MIN(gdk_pixbuf_get_height(dst), gdk_pixbuf_get_height(src))),
+           canvas_x - offset_x, canvas_y - offset_y,
+           1.0, 1.0, GDK_INTERP_NEAREST, 255);
+      }
+      break;
+    default:
+      break;
+  } 
+}
+
+/****************************************************************************
+  Draw a full sprite onto the mapview or citydialog canvas.
+****************************************************************************/
+void canvas_put_sprite_full(struct canvas *pcanvas,
+                           int canvas_x, int canvas_y,
+                           struct Sprite *sprite)
+{
+  canvas_put_sprite(pcanvas, canvas_x, canvas_y, sprite,
+                   0, 0, sprite->width, sprite->height);
+}
+
+/****************************************************************************
+  Draw a full sprite onto the canvas.  If "fog" is specified draw it with
+  fog.
+****************************************************************************/
+void canvas_put_sprite_fogged(struct canvas *pcanvas,
+                             int canvas_x, int canvas_y,
+                             struct Sprite *psprite,
+                             bool fog, int fog_x, int fog_y)
+{
+  if (pcanvas->type == CANVAS_PIXMAP) {
+    pixmap_put_overlay_tile_draw(pcanvas->v.pixmap, canvas_x, canvas_y,
+                                psprite, fog);
+  }
+}
+
+/****************************************************************************
+  Draw a filled-in colored rectangle onto the mapview or citydialog canvas.
+****************************************************************************/
+void canvas_put_rectangle(struct canvas *pcanvas,
+                         enum color_std color,
+                         int canvas_x, int canvas_y, int width, int height)
+{
+  GdkColor *col = colors_standard[color];
+
+  switch (pcanvas->type) {
+    case CANVAS_PIXMAP:
+      gdk_gc_set_foreground(fill_bg_gc, col);
+      gdk_draw_rectangle(pcanvas->v.pixmap, fill_bg_gc, TRUE,
+         canvas_x, canvas_y, width, height);
+      break;
+    case CANVAS_PIXCOMM:
+      gtk_pixcomm_fill(pcanvas->v.pixcomm, col);
+      break;
+    case CANVAS_PIXBUF:
+      gdk_pixbuf_fill(pcanvas->v.pixbuf,
+         ((guint32)(col->red & 0xff00) << 16)
+         | ((col->green & 0xff00) << 8) | (col->blue & 0xff00) | 0xff);
+      break;
+    default:
+      break;
+  }
+}
+
+/****************************************************************************
+  Fill the area covered by the sprite with the given color.
+****************************************************************************/
+void canvas_fill_sprite_area(struct canvas *pcanvas,
+                            struct Sprite *psprite, enum color_std color,
+                            int canvas_x, int canvas_y)
+{
+  if (pcanvas->type == CANVAS_PIXMAP) {
+    gdk_gc_set_clip_origin(fill_bg_gc, canvas_x, canvas_y);
+    gdk_gc_set_clip_mask(fill_bg_gc, sprite_get_mask(psprite));
+    gdk_gc_set_foreground(fill_bg_gc, colors_standard[color]);
+
+    gdk_draw_rectangle(pcanvas->v.pixmap, fill_bg_gc, TRUE,
+                      canvas_x, canvas_y, psprite->width, psprite->height);
+
+    gdk_gc_set_clip_mask(fill_bg_gc, NULL);
+  }
+}
+
+/****************************************************************************
+  Fill the area covered by the sprite with the given color.
+****************************************************************************/
+void canvas_fog_sprite_area(struct canvas *pcanvas, struct Sprite *psprite,
+                           int canvas_x, int canvas_y)
+{
+  if (pcanvas->type == CANVAS_PIXMAP) {
+    gdk_gc_set_clip_origin(fill_tile_gc, canvas_x, canvas_y);
+    gdk_gc_set_clip_mask(fill_tile_gc, sprite_get_mask(psprite));
+    gdk_gc_set_foreground(fill_tile_gc, colors_standard[COLOR_STD_BLACK]);
+    gdk_gc_set_stipple(fill_tile_gc, black50);
+    gdk_gc_set_ts_origin(fill_tile_gc, canvas_x, canvas_y);
+
+    gdk_draw_rectangle(pcanvas->v.pixmap, fill_tile_gc, TRUE,
+                      canvas_x, canvas_y, psprite->width, psprite->height);
+
+    gdk_gc_set_clip_mask(fill_tile_gc, NULL); 
+  }
+}
+
+/****************************************************************************
+  Draw a colored line onto the mapview or citydialog canvas.
+****************************************************************************/
+void canvas_put_line(struct canvas *pcanvas, enum color_std color,
+                    enum line_type ltype, int start_x, int start_y,
+                    int dx, int dy)
+{
+  if (pcanvas->type == CANVAS_PIXMAP) {
+    GdkGC *gc = NULL;
+
+    switch (ltype) {
+    case LINE_NORMAL:
+      gc = thin_line_gc;
+      break;
+    case LINE_BORDER:
+      gc = border_line_gc;
+      break;
+    case LINE_TILE_FRAME:
+      gc = thick_line_gc;
+      break;
+    case LINE_GOTO:
+      gc = thick_line_gc;
+      break;
+    }
+
+    gdk_gc_set_foreground(gc, colors_standard[color]);
+    gdk_draw_line(pcanvas->v.pixmap, gc,
+                 start_x, start_y, start_x + dx, start_y + dy);
+  }
+}
+
+static PangoLayout *layout;
+static PangoFontDescription **fonts[FONT_COUNT] = {&main_font,
+                                                  &city_productions_font};
+
+/****************************************************************************
+  Return the size of the given text in the given font.  This size should
+  include the ascent and descent of the text.  Either of width or height
+  may be NULL in which case those values simply shouldn't be filled out.
+****************************************************************************/
+void get_text_size(int *width, int *height,
+                  enum client_font font, const char *text)
+{
+  PangoRectangle rect;
+
+  if (!layout) {
+    layout = pango_layout_new(gdk_pango_context_get());
+  }
+
+  pango_layout_set_font_description(layout, *fonts[font]);
+  pango_layout_set_text(layout, text, -1);
+
+  pango_layout_get_pixel_extents(layout, &rect, NULL);
+  if (width) {
+    *width = rect.width;
+  }
+  if (height) {
+    *height = rect.height;
+  }
+}
+
+/****************************************************************************
+  Draw the text onto the canvas in the given color and font.  The canvas
+  position does not account for the ascent of the text; this function must
+  take care of this manually.  The text will not be NULL but may be empty.
+****************************************************************************/
+void canvas_put_text(struct canvas *pcanvas, int canvas_x, int canvas_y,
+                    enum client_font font, enum color_std color,
+                    const char *text)
+{
+  PangoRectangle rect;
+
+  if (pcanvas->type != CANVAS_PIXMAP) {
+    return;
+  }
+  if (!layout) {
+    layout = pango_layout_new(gdk_pango_context_get());
+  }
+
+  gdk_gc_set_foreground(civ_gc, colors_standard[color]);
+  pango_layout_set_font_description(layout, *fonts[font]);
+  pango_layout_set_text(layout, text, -1);
+
+  pango_layout_get_pixel_extents(layout, &rect, NULL);
+  gtk_draw_shadowed_string(pcanvas->v.pixmap,
+                          toplevel->style->black_gc, civ_gc,
+                          canvas_x,
+                          canvas_y + PANGO_ASCENT(rect), layout);
+}
Index: client/gui-gtk-2.0/canvas.h
===================================================================
RCS file: client/gui-gtk-2.0/canvas.h
diff -N client/gui-gtk-2.0/canvas.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ client/gui-gtk-2.0/canvas.h 22 Mar 2005 00:01:58 -0000
@@ -0,0 +1,39 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+#ifndef FC__CANVAS_H
+#define FC__CANVAS_H
+
+#include <gtk/gtk.h>
+
+#include "canvas_g.h"
+
+#include "gtkpixcomm.h"
+
+enum canvas_type {
+  CANVAS_PIXMAP,
+  CANVAS_PIXCOMM,
+  CANVAS_PIXBUF
+};
+
+struct canvas
+{
+  enum canvas_type type;
+
+  union {
+    GdkPixmap *pixmap;
+    GtkPixcomm *pixcomm;
+    GdkPixbuf *pixbuf;
+  } v;
+};
+
+#endif  /* FC__CANVAS_H */
Index: client/gui-gtk-2.0/graphics.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/graphics.c,v
retrieving revision 1.40
diff -u -r1.40 graphics.c
--- client/gui-gtk-2.0/graphics.c       21 Mar 2005 16:37:52 -0000      1.40
+++ client/gui-gtk-2.0/graphics.c       22 Mar 2005 00:01:58 -0000
@@ -81,103 +81,6 @@
   gdk_draw_layout(drawable, white_gc, x, y, layout);
 }
 
-/****************************************************************************
-  Create a new sprite by cropping and taking only the given portion of
-  the image.
-
-  source gives the sprite that is to be cropped.
-
-  x,y, width, height gives the rectangle to be cropped.  The pixel at
-  position of the source sprite will be at (0,0) in the new sprite, and
-  the new sprite will have dimensions (width, height).
-
-  mask gives an additional mask to be used for clipping the new sprite.
-
-  mask_offset_x, mask_offset_y is the offset of the mask relative to the
-  origin of the source image.  The pixel at (mask_offset_x,mask_offset_y)
-  in the mask image will be used to clip pixel (0,0) in the source image
-  which is pixel (-x,-y) in the new image.
-****************************************************************************/
-struct Sprite *crop_sprite(struct Sprite *source,
-                          int x, int y,
-                          int width, int height,
-                          struct Sprite *mask,
-                          int mask_offset_x, int mask_offset_y)
-{
-  GdkPixbuf *mypixbuf, *sub, *mask_pixbuf;
-
-  /* First just crop the image. */
-  if (x < 0) {
-    width += x;
-    x = 0;
-  }
-  if (y < 0) {
-    height += y;
-    y = 0;
-  }
-  width = CLIP(0, width, source->width - x);
-  height = CLIP(0, height, source->height - y);
-  sub = gdk_pixbuf_new_subpixbuf(sprite_get_pixbuf(source), x, y,
-                                width, height);
-  mypixbuf = gdk_pixbuf_copy(sub);
-  g_object_unref(sub);
-
-  /* Now mask.  This reduces the alpha of the final image proportional to the
-   * alpha of the mask.  Thus if the mask has 50% alpha the final image will
-   * be reduced by 50% alpha.  Note that the mask offset is in coordinates
-   * relative to the clipped image not the final image. */
-  if (mask
-      && (mask_pixbuf = sprite_get_pixbuf(mask))
-      && gdk_pixbuf_get_has_alpha(mask_pixbuf)) {
-    int x1, y1;
-
-    /* The mask offset is the offset of the mask relative to the origin
-     * of the original source image.  For instance when cropping with
-     * blending sprites the offset is always 0.  Here we convert the
-     * coordinates so that they are relative to the origin of the new
-     * (cropped) image. */
-    mask_offset_x -= x;
-    mask_offset_y -= y;
-
-    width = CLIP(0, width, mask->width + mask_offset_x);
-    height = CLIP(0, height, mask->height + mask_offset_y);
-
-    if (!gdk_pixbuf_get_has_alpha(mypixbuf)) {
-      GdkPixbuf *p2 = mypixbuf;
-
-      mypixbuf = gdk_pixbuf_add_alpha(mypixbuf, FALSE, 0, 0, 0);
-      g_object_unref(p2);
-    }
-
-    for (x1 = 0; x1 < width; x1++) {
-      for (y1 = 0; y1 < height; y1++) {
-       int mask_x = x1 - mask_offset_x, mask_y = y1 - mask_offset_y;
-       guchar *alpha = gdk_pixbuf_get_pixels(mypixbuf)
-         + y1 * gdk_pixbuf_get_rowstride(mypixbuf)
-         + x1 * gdk_pixbuf_get_n_channels(mypixbuf)
-         + 3;
-       guchar *mask_alpha = gdk_pixbuf_get_pixels(mask_pixbuf)
-         + mask_y * gdk_pixbuf_get_rowstride(mask_pixbuf)
-         + mask_x * gdk_pixbuf_get_n_channels(mask_pixbuf)
-         + 3;
-
-       *alpha = (*alpha) * (*mask_alpha) / 255;
-      }
-    }
-  }
-
-  return ctor_sprite(mypixbuf);
-}
-
-/****************************************************************************
-  Find the dimensions of the sprite.
-****************************************************************************/
-void get_sprite_dimensions(struct Sprite *sprite, int *width, int *height)
-{
-  *width = sprite->width;
-  *height = sprite->height;
-}
-
 /***************************************************************************
 ...
 ***************************************************************************/
@@ -197,126 +100,6 @@
 }
 
 /***************************************************************************
- Create a new sprite with the given pixmap, dimensions, and
- (optional) mask.
-***************************************************************************/
-SPRITE *ctor_sprite(GdkPixbuf *pixbuf)
-{
-  struct Sprite *sprite = fc_malloc(sizeof(*sprite));
-  bool has_alpha = FALSE, has_mask = FALSE;
-
-  sprite->width = gdk_pixbuf_get_width(pixbuf);
-  sprite->height = gdk_pixbuf_get_height(pixbuf);
-
-  /* Check to see if this pixbuf has an alpha layer. */
-  if (gdk_pixbuf_get_has_alpha(pixbuf)) {
-    guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
-    int x, y, rowstride = gdk_pixbuf_get_rowstride(pixbuf);
-
-    for (y = 0; y < sprite->height; y++) {
-      for (x = 0; x < sprite->width; x++) {
-       int i = y * rowstride + 4 * x + 3;
-       guchar pixel = pixels[i];
-
-       if (pixel > 0 && pixel < 255) {
-         has_alpha = TRUE;
-       }
-       if (pixel == 0) {
-         has_mask = TRUE;
-       }
-      }
-    }
-  }
-
-  sprite->pixbuf_fogged = NULL;
-  sprite->pixmap_fogged = NULL;
-  if (has_alpha) {
-    sprite->pixbuf = pixbuf;
-    sprite->pixmap = NULL;
-    sprite->mask = NULL;
-  } else {
-    gdk_pixbuf_render_pixmap_and_mask(pixbuf, &sprite->pixmap,
-                                     &sprite->mask, 1);
-    if (!has_mask && sprite->mask) {
-      g_object_unref(sprite->mask);
-      sprite->mask = NULL;
-    }
-    g_object_unref(pixbuf);
-    sprite->pixbuf = NULL;
-  }
-  return sprite;
-}
-
-
-#ifdef UNUSED
-/***************************************************************************
-...
-***************************************************************************/
-void dtor_sprite( SPRITE *mysprite )
-{
-    free_sprite( mysprite );
-    return;
-}
-#endif
-
-/***************************************************************************
- Returns the filename extensions the client supports
- Order is important.
-***************************************************************************/
-const char **gfx_fileextensions(void)
-{
-  static const char *ext[] =
-  {
-    "png",
-    "xpm",
-    NULL
-  };
-
-  return ext;
-}
-
-/***************************************************************************
-...
-***************************************************************************/
-struct Sprite *load_gfxfile(const char *filename)
-{
-  GdkPixbuf *im;
-
-  if (!(im = gdk_pixbuf_new_from_file(filename, NULL))) {
-    freelog(LOG_FATAL, "Failed reading graphics file: %s", filename);
-    exit(EXIT_FAILURE);
-  }
-
-  return ctor_sprite(im);
-}
-
-/***************************************************************************
-   Deletes a sprite.  These things can use a lot of memory.
-***************************************************************************/
-void free_sprite(SPRITE * s)
-{
-  if (s->pixmap) {
-    g_object_unref(s->pixmap);
-    s->pixmap = NULL;
-  }
-  if (s->mask) {
-    g_object_unref(s->mask);
-    s->mask = NULL;
-  }
-  if (s->pixbuf) {
-    g_object_unref(s->pixbuf);
-    s->pixbuf = NULL;
-  }
-  if (s->pixmap_fogged) {
-    g_object_unref(s->pixmap_fogged);
-  }
-  if (s->pixbuf_fogged) {
-    g_object_unref(s->pixbuf_fogged);
-  }
-  free(s);
-}
-
-/***************************************************************************
  ...
 ***************************************************************************/
 void create_overlay_unit(struct canvas *pcanvas, int i)
@@ -362,177 +145,3 @@
     radar_gfx_sprite=NULL;
   }
 }
-
-/***************************************************************************
-  Scales a sprite. If the sprite contains a mask, the mask is scaled
-  as as well.
-***************************************************************************/
-SPRITE* sprite_scale(SPRITE *src, int new_w, int new_h)
-{
-  return ctor_sprite(gdk_pixbuf_scale_simple(sprite_get_pixbuf(src),
-                                            new_w, new_h,
-                                            GDK_INTERP_BILINEAR));
-}
-
-/***************************************************************************
- 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;
-  GdkBitmap *mask = sprite_get_mask(sprite);
-  int i, j;
-
-  if (!mask) {
-    *start_x = 0;
-    *start_y = 0;
-    *end_x = sprite->width - 1;
-    *end_y = sprite->height - 1;
-    return;
-  }
-
-  mask_image =
-    gdk_drawable_get_image(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;
-      }
-    }
-  }
-
-  g_object_unref(mask_image);
-}
-
-/*********************************************************************
- Crops all blankspace from a sprite (insofar as is possible as a rectangle)
-*********************************************************************/
-SPRITE *crop_blankspace(SPRITE *s)
-{
-  int x1, y1, x2, y2;
-
-  sprite_get_bounding_box(s, &x1, &y1, &x2, &y2);
-
-  return crop_sprite(s, x1, y1, x2 - x1 + 1, y2 - y1 + 1, NULL, -1, -1);
-}
-
-/*********************************************************************
-  Converts a pixmap/mask sprite to a GdkPixbuf.
-
-  This is just a helper function for sprite_get_pixbuf().  Most callers
-  should use that function instead.
-*********************************************************************/
-static GdkPixbuf *gdk_pixbuf_new_from_pixmap_sprite(SPRITE *src)
-{
-  GdkPixbuf *dst;
-  int w, h;
-
-  w = src->width;
-  h = src->height;
-  
-  /* convert pixmap */
-  dst = gdk_pixbuf_new(GDK_COLORSPACE_RGB, src->mask != NULL, 8, w, h);
-  gdk_pixbuf_get_from_drawable(dst, src->pixmap, NULL, 0, 0, 0, 0, w, h);
-
-  /* convert mask */
-  if (src->mask) {
-    GdkImage *img;
-    int x, y, rowstride;
-    guchar *pixels;
-
-    img = gdk_drawable_get_image(src->mask, 0, 0, w, h);
-
-    pixels = gdk_pixbuf_get_pixels(dst);
-    rowstride = gdk_pixbuf_get_rowstride(dst);
-
-    for (y = 0; y < h; y++) {
-      for (x = 0; x < w; x++) {
-       guchar *pixel = pixels + y * rowstride + x * 4 + 3;
-
-       if (gdk_image_get_pixel(img, x, y)) {
-         *pixel = 255;
-       } else {
-         *pixel = 0;
-       }
-      }
-    }
-    g_object_unref(img);
-  }
-
-  return dst;
-}
-
-/********************************************************************
-  Render a pixbuf from the sprite.
-
- NOTE: the pixmap and mask of a sprite must not change after this
-       function is called!
-********************************************************************/
-GdkPixbuf *sprite_get_pixbuf(SPRITE *sprite)
-{
-  if (!sprite) {
-    return NULL;
-  }
-  
-  if (!sprite->pixbuf) {
-    sprite->pixbuf = gdk_pixbuf_new_from_pixmap_sprite(sprite);
-  }
-  return sprite->pixbuf;
-}
-
-/****************************************************************************
-  Render a mask from the sprite.
-
-  NOTE: the pixbuf of a sprite must not change after this function is called!
-****************************************************************************/
-GdkBitmap *sprite_get_mask(struct Sprite *sprite)
-{
-  if (!sprite->pixmap && !sprite->mask) {
-    /* If we're not in pixmap mode and we don't yet have a mask, render
-     * the pixbuf to a mask. */
-    gdk_pixbuf_render_pixmap_and_mask(sprite->pixbuf, NULL,
-                                     &sprite->mask, 1);
-  }
-  return sprite->mask;
-}
Index: client/gui-gtk-2.0/graphics.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/graphics.h,v
retrieving revision 1.14
diff -u -r1.14 graphics.h
--- client/gui-gtk-2.0/graphics.h       11 Mar 2005 22:40:53 -0000      1.14
+++ client/gui-gtk-2.0/graphics.h       22 Mar 2005 00:01:58 -0000
@@ -18,22 +18,8 @@
 #include "graphics_g.h"
 #include "mapview_common.h"
 
-struct Sprite
-{
-  /* A pixmap + mask is used if there's a 1-bit alpha channel.  mask may be
-   * NULL if there's no alpha.  For multi-bit alpha levels, a pixbuf will be
-   * used instead.  For consistency a pixbuf may be generated on-demand when
-   * doing drawing (into a gtkpixcomm or gtkimage), so it's important that
-   * the sprite data not be changed after the sprite is loaded. */
-  GdkPixmap *pixmap, *pixmap_fogged;
-  GdkBitmap *mask;
-  GdkPixbuf *pixbuf, *pixbuf_fogged;
-
-  int       width;
-  int       height;
-};
-
-typedef struct Sprite SPRITE;
+#include "canvas.h"
+#include "sprite.h"
 
 void create_overlay_unit(struct canvas *pcanvas, int i);
 
@@ -49,17 +35,5 @@
                              GdkGC *white_gc,
                              gint x, gint y, PangoLayout *layout);
 
-SPRITE *ctor_sprite(GdkPixbuf *pixbuf);
-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);
-SPRITE *crop_blankspace(SPRITE *s);
-
-/********************************************************************
-  Note: a sprite cannot be changed after these functions are called!
- ********************************************************************/
-GdkPixbuf *sprite_get_pixbuf(SPRITE *sprite);
-GdkBitmap *sprite_get_mask(struct Sprite *sprite);
-
 #endif  /* FC__GRAPHICS_H */
 
Index: client/gui-gtk-2.0/gtkpixcomm.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/gtkpixcomm.c,v
retrieving revision 1.14
diff -u -r1.14 gtkpixcomm.c
--- client/gui-gtk-2.0/gtkpixcomm.c     1 Feb 2005 01:08:45 -0000       1.14
+++ client/gui-gtk-2.0/gtkpixcomm.c     22 Mar 2005 00:01:58 -0000
@@ -37,6 +37,8 @@
 #include <config.h>
 #endif
 
+#include <math.h>
+
 #include "gui_main.h"
 #include "gtkpixcomm.h"
 
Index: client/gui-gtk-2.0/gtkpixcomm.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/gtkpixcomm.h,v
retrieving revision 1.4
diff -u -r1.4 gtkpixcomm.h
--- client/gui-gtk-2.0/gtkpixcomm.h     15 Dec 2004 23:18:18 -0000      1.4
+++ client/gui-gtk-2.0/gtkpixcomm.h     22 Mar 2005 00:01:58 -0000
@@ -29,7 +29,8 @@
 
 
 #include <gtk/gtkmisc.h>
-#include "graphics.h"
+
+#include "sprite.h"
 
 
 G_BEGIN_DECLS
Index: client/gui-gtk-2.0/gui_main.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/gui_main.h,v
retrieving revision 1.22
diff -u -r1.22 gui_main.h
--- client/gui-gtk-2.0/gui_main.h       14 Feb 2005 17:52:56 -0000      1.22
+++ client/gui-gtk-2.0/gui_main.h       22 Mar 2005 00:01:58 -0000
@@ -18,23 +18,6 @@
 #include "gtkpixcomm.h"
 #include "gui_main_g.h"
 
-enum canvas_type {
-  CANVAS_PIXMAP,
-  CANVAS_PIXCOMM,
-  CANVAS_PIXBUF
-};
-
-struct canvas
-{
-  enum canvas_type type;
-
-  union {
-    GdkPixmap *pixmap;
-    GtkPixcomm *pixcomm;
-    GdkPixbuf *pixbuf;
-  } v;
-};
-
 /* network string charset conversion */
 gchar *ntoh_str(const gchar *netstr);
 
Index: client/gui-gtk-2.0/mapview.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/mapview.c,v
retrieving revision 1.167
diff -u -r1.167 mapview.c
--- client/gui-gtk-2.0/mapview.c        21 Mar 2005 16:37:52 -0000      1.167
+++ client/gui-gtk-2.0/mapview.c        22 Mar 2005 00:01:58 -0000
@@ -55,15 +55,6 @@
 
 #define map_canvas_store (mapview.store->v.pixmap)
 
-static void pixmap_put_overlay_tile(GdkDrawable *pixmap,
-                                   int canvas_x, int canvas_y,
-                                   struct Sprite *ssprite);
-
-static void pixmap_put_overlay_tile_draw(GdkDrawable *pixmap,
-                                        int canvas_x, int canvas_y,
-                                        struct Sprite *ssprite,
-                                        bool fog);
-
 static GtkObject *map_hadj, *map_vadj;
 
 
@@ -246,29 +237,6 @@
   update_map_canvas_scrollbars_size();
 }
 
-/**************************************************************************
-...
-**************************************************************************/
-struct canvas *canvas_create(int width, int height)
-{
-  struct canvas *result = fc_malloc(sizeof(*result));
-
-  result->type = CANVAS_PIXMAP;
-  result->v.pixmap = gdk_pixmap_new(root_window, width, height, -1);
-  return result;
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-void canvas_free(struct canvas *store)
-{
-  if (store->type == CANVAS_PIXMAP) {
-    g_object_unref(store->v.pixmap);
-  }
-  free(store);
-}
-
 /****************************************************************************
   Return a canvas that is the overview window.
 ****************************************************************************/
@@ -461,65 +429,6 @@
   update_map_canvas_visible();
 }
 
-static PangoLayout *layout;
-static PangoFontDescription **fonts[FONT_COUNT] = {&main_font,
-                                                  &city_productions_font};
-
-/****************************************************************************
-  Return the size of the given text in the given font.  This size should
-  include the ascent and descent of the text.  Either of width or height
-  may be NULL in which case those values simply shouldn't be filled out.
-****************************************************************************/
-void get_text_size(int *width, int *height,
-                  enum client_font font, const char *text)
-{
-  PangoRectangle rect;
-
-  if (!layout) {
-    layout = pango_layout_new(gdk_pango_context_get());
-  }
-
-  pango_layout_set_font_description(layout, *fonts[font]);
-  pango_layout_set_text(layout, text, -1);
-
-  pango_layout_get_pixel_extents(layout, &rect, NULL);
-  if (width) {
-    *width = rect.width;
-  }
-  if (height) {
-    *height = rect.height;
-  }
-}
-
-/****************************************************************************
-  Draw the text onto the canvas in the given color and font.  The canvas
-  position does not account for the ascent of the text; this function must
-  take care of this manually.  The text will not be NULL but may be empty.
-****************************************************************************/
-void canvas_put_text(struct canvas *pcanvas, int canvas_x, int canvas_y,
-                    enum client_font font, enum color_std color,
-                    const char *text)
-{
-  PangoRectangle rect;
-
-  if (pcanvas->type != CANVAS_PIXMAP) {
-    return;
-  }
-  if (!layout) {
-    layout = pango_layout_new(gdk_pango_context_get());
-  }
-
-  gdk_gc_set_foreground(civ_gc, colors_standard[color]);
-  pango_layout_set_font_description(layout, *fonts[font]);
-  pango_layout_set_text(layout, text, -1);
-
-  pango_layout_get_pixel_extents(layout, &rect, NULL);
-  gtk_draw_shadowed_string(pcanvas->v.pixmap,
-                          toplevel->style->black_gc, civ_gc,
-                          canvas_x,
-                          canvas_y + PANGO_ASCENT(rect), layout);
-}
-
 /**************************************************************************
 ...
 **************************************************************************/
@@ -562,9 +471,9 @@
 /**************************************************************************
 ...
 **************************************************************************/
-static void pixmap_put_overlay_tile(GdkDrawable *pixmap,
-                                   int canvas_x, int canvas_y,
-                                   struct Sprite *ssprite)
+void pixmap_put_overlay_tile(GdkDrawable *pixmap,
+                            int canvas_x, int canvas_y,
+                            struct Sprite *ssprite)
 {
   if (!ssprite) {
     return;
@@ -621,192 +530,6 @@
 }
 
 /**************************************************************************
-  Draw some or all of a sprite onto the mapview or citydialog canvas.
-**************************************************************************/
-void canvas_put_sprite(struct canvas *pcanvas,
-                      int canvas_x, int canvas_y,
-                      struct Sprite *sprite,
-                      int offset_x, int offset_y, int width, int height)
-{
-  switch (pcanvas->type) {
-    case CANVAS_PIXMAP:
-      pixmap_put_sprite(pcanvas->v.pixmap, canvas_x, canvas_y,
-         sprite, offset_x, offset_y, width, height);
-      break;
-    case CANVAS_PIXCOMM:
-      gtk_pixcomm_copyto(pcanvas->v.pixcomm, sprite, canvas_x, canvas_y);
-      break;
-    case CANVAS_PIXBUF:
-      {
-       GdkPixbuf *src, *dst;
-
-       /* FIXME: is this right??? */
-       if (canvas_x < 0) {
-         offset_x -= canvas_x;
-         canvas_x = 0;
-       }
-       if (canvas_y < 0) {
-         offset_y -= canvas_y;
-         canvas_y = 0;
-       }
-
-
-       src = sprite_get_pixbuf(sprite);
-       dst = pcanvas->v.pixbuf;
-       gdk_pixbuf_composite(src, dst, canvas_x, canvas_y,
-           MIN(width,
-             MIN(gdk_pixbuf_get_width(dst), gdk_pixbuf_get_width(src))),
-           MIN(height,
-             MIN(gdk_pixbuf_get_height(dst), gdk_pixbuf_get_height(src))),
-           canvas_x - offset_x, canvas_y - offset_y,
-           1.0, 1.0, GDK_INTERP_NEAREST, 255);
-      }
-      break;
-    default:
-      break;
-  } 
-}
-
-/**************************************************************************
-  Draw a full sprite onto the mapview or citydialog canvas.
-**************************************************************************/
-void canvas_put_sprite_full(struct canvas *pcanvas,
-                           int canvas_x, int canvas_y,
-                           struct Sprite *sprite)
-{
-  canvas_put_sprite(pcanvas, canvas_x, canvas_y, sprite,
-                   0, 0, sprite->width, sprite->height);
-}
-
-/****************************************************************************
-  Draw a full sprite onto the canvas.  If "fog" is specified draw it with
-  fog.
-****************************************************************************/
-void canvas_put_sprite_fogged(struct canvas *pcanvas,
-                             int canvas_x, int canvas_y,
-                             struct Sprite *psprite,
-                             bool fog, int fog_x, int fog_y)
-{
-  if (pcanvas->type == CANVAS_PIXMAP) {
-    pixmap_put_overlay_tile_draw(pcanvas->v.pixmap, canvas_x, canvas_y,
-                                psprite, fog);
-  }
-}
-
-/**************************************************************************
-  Draw a filled-in colored rectangle onto the mapview or citydialog canvas.
-**************************************************************************/
-void canvas_put_rectangle(struct canvas *pcanvas,
-                         enum color_std color,
-                         int canvas_x, int canvas_y, int width, int height)
-{
-  GdkColor *col = colors_standard[color];
-
-  switch (pcanvas->type) {
-    case CANVAS_PIXMAP:
-      gdk_gc_set_foreground(fill_bg_gc, col);
-      gdk_draw_rectangle(pcanvas->v.pixmap, fill_bg_gc, TRUE,
-         canvas_x, canvas_y, width, height);
-      break;
-    case CANVAS_PIXCOMM:
-      gtk_pixcomm_fill(pcanvas->v.pixcomm, col);
-      break;
-    case CANVAS_PIXBUF:
-      gdk_pixbuf_fill(pcanvas->v.pixbuf,
-         ((guint32)(col->red & 0xff00) << 16)
-         | ((col->green & 0xff00) << 8) | (col->blue & 0xff00) | 0xff);
-      break;
-    default:
-      break;
-  }
-}
-
-/****************************************************************************
-  Fill the area covered by the sprite with the given color.
-****************************************************************************/
-void canvas_fill_sprite_area(struct canvas *pcanvas,
-                            struct Sprite *psprite, enum color_std color,
-                            int canvas_x, int canvas_y)
-{
-  if (pcanvas->type == CANVAS_PIXMAP) {
-    gdk_gc_set_clip_origin(fill_bg_gc, canvas_x, canvas_y);
-    gdk_gc_set_clip_mask(fill_bg_gc, sprite_get_mask(psprite));
-    gdk_gc_set_foreground(fill_bg_gc, colors_standard[color]);
-
-    gdk_draw_rectangle(pcanvas->v.pixmap, fill_bg_gc, TRUE,
-                      canvas_x, canvas_y, psprite->width, psprite->height);
-
-    gdk_gc_set_clip_mask(fill_bg_gc, NULL);
-  }
-}
-
-/****************************************************************************
-  Fill the area covered by the sprite with the given color.
-****************************************************************************/
-void canvas_fog_sprite_area(struct canvas *pcanvas, struct Sprite *psprite,
-                           int canvas_x, int canvas_y)
-{
-  if (pcanvas->type == CANVAS_PIXMAP) {
-    gdk_gc_set_clip_origin(fill_tile_gc, canvas_x, canvas_y);
-    gdk_gc_set_clip_mask(fill_tile_gc, sprite_get_mask(psprite));
-    gdk_gc_set_foreground(fill_tile_gc, colors_standard[COLOR_STD_BLACK]);
-    gdk_gc_set_stipple(fill_tile_gc, black50);
-    gdk_gc_set_ts_origin(fill_tile_gc, canvas_x, canvas_y);
-
-    gdk_draw_rectangle(pcanvas->v.pixmap, fill_tile_gc, TRUE,
-                      canvas_x, canvas_y, psprite->width, psprite->height);
-
-    gdk_gc_set_clip_mask(fill_tile_gc, NULL); 
-  }
-}
-
-/**************************************************************************
-  Draw a colored line onto the mapview or citydialog canvas.
-**************************************************************************/
-void canvas_put_line(struct canvas *pcanvas, enum color_std color,
-                    enum line_type ltype, int start_x, int start_y,
-                    int dx, int dy)
-{
-  if (pcanvas->type == CANVAS_PIXMAP) {
-    GdkGC *gc = NULL;
-
-    switch (ltype) {
-    case LINE_NORMAL:
-      gc = thin_line_gc;
-      break;
-    case LINE_BORDER:
-      gc = border_line_gc;
-      break;
-    case LINE_TILE_FRAME:
-      gc = thick_line_gc;
-      break;
-    case LINE_GOTO:
-      gc = thick_line_gc;
-      break;
-    }
-
-    gdk_gc_set_foreground(gc, colors_standard[color]);
-    gdk_draw_line(pcanvas->v.pixmap, gc,
-                 start_x, start_y, start_x + dx, start_y + dy);
-  }
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-void canvas_copy(struct canvas *dest, struct canvas *src,
-                int src_x, int src_y, int dest_x, int dest_y,
-                int width, int height)
-{
-  if (dest->type == src->type) {
-    if (src->type == CANVAS_PIXMAP) {
-      gdk_draw_drawable(dest->v.pixmap, fill_bg_gc, src->v.pixmap,
-                       src_x, src_y, dest_x, dest_y, width, height);
-    }
-  }
-}
-
-/**************************************************************************
   Created a fogged version of the sprite.  This can fail on older systems
   in which case the callers needs a fallback.
 **************************************************************************/
@@ -851,10 +574,10 @@
 /**************************************************************************
 Only used for isometric view.
 **************************************************************************/
-static void pixmap_put_overlay_tile_draw(GdkDrawable *pixmap,
-                                        int canvas_x, int canvas_y,
-                                        struct Sprite *ssprite,
-                                        bool fog)
+void pixmap_put_overlay_tile_draw(GdkDrawable *pixmap,
+                                 int canvas_x, int canvas_y,
+                                 struct Sprite *ssprite,
+                                 bool fog)
 {
   if (!ssprite) {
     return;
Index: client/gui-gtk-2.0/mapview.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/mapview.h,v
retrieving revision 1.19
diff -u -r1.19 mapview.h
--- client/gui-gtk-2.0/mapview.h        1 Feb 2005 01:08:45 -0000       1.19
+++ client/gui-gtk-2.0/mapview.h        22 Mar 2005 00:01:58 -0000
@@ -21,6 +21,7 @@
 #include "mapview_g.h"
 #include "mapview_common.h"
 
+#include "canvas.h"
 #include "graphics.h"
 
 struct unit;
@@ -40,4 +41,13 @@
 void scrollbar_jump_callback(GtkAdjustment *adj, gpointer hscrollbar);
 void update_map_canvas_scrollbars_size(void);
 
+void pixmap_put_overlay_tile(GdkDrawable *pixmap,
+                            int canvas_x, int canvas_y,
+                            struct Sprite *ssprite);
+
+void pixmap_put_overlay_tile_draw(GdkDrawable *pixmap,
+                                 int canvas_x, int canvas_y,
+                                 struct Sprite *ssprite,
+                                 bool fog);
+
 #endif  /* FC__MAPVIEW_H */
Index: client/gui-gtk-2.0/sprite.c
===================================================================
RCS file: client/gui-gtk-2.0/sprite.c
diff -N client/gui-gtk-2.0/sprite.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ client/gui-gtk-2.0/sprite.c 22 Mar 2005 00:01:58 -0000
@@ -0,0 +1,403 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "log.h"
+
+#include "sprite.h"
+
+/****************************************************************************
+  Create a new sprite by cropping and taking only the given portion of
+  the image.
+
+  source gives the sprite that is to be cropped.
+
+  x,y, width, height gives the rectangle to be cropped.  The pixel at
+  position of the source sprite will be at (0,0) in the new sprite, and
+  the new sprite will have dimensions (width, height).
+
+  mask gives an additional mask to be used for clipping the new sprite.
+
+  mask_offset_x, mask_offset_y is the offset of the mask relative to the
+  origin of the source image.  The pixel at (mask_offset_x,mask_offset_y)
+  in the mask image will be used to clip pixel (0,0) in the source image
+  which is pixel (-x,-y) in the new image.
+****************************************************************************/
+struct Sprite *crop_sprite(struct Sprite *source,
+                          int x, int y,
+                          int width, int height,
+                          struct Sprite *mask,
+                          int mask_offset_x, int mask_offset_y)
+{
+  GdkPixbuf *mypixbuf, *sub, *mask_pixbuf;
+
+  /* First just crop the image. */
+  if (x < 0) {
+    width += x;
+    x = 0;
+  }
+  if (y < 0) {
+    height += y;
+    y = 0;
+  }
+  width = CLIP(0, width, source->width - x);
+  height = CLIP(0, height, source->height - y);
+  sub = gdk_pixbuf_new_subpixbuf(sprite_get_pixbuf(source), x, y,
+                                width, height);
+  mypixbuf = gdk_pixbuf_copy(sub);
+  g_object_unref(sub);
+
+  /* Now mask.  This reduces the alpha of the final image proportional to the
+   * alpha of the mask.  Thus if the mask has 50% alpha the final image will
+   * be reduced by 50% alpha.  Note that the mask offset is in coordinates
+   * relative to the clipped image not the final image. */
+  if (mask
+      && (mask_pixbuf = sprite_get_pixbuf(mask))
+      && gdk_pixbuf_get_has_alpha(mask_pixbuf)) {
+    int x1, y1;
+
+    /* The mask offset is the offset of the mask relative to the origin
+     * of the original source image.  For instance when cropping with
+     * blending sprites the offset is always 0.  Here we convert the
+     * coordinates so that they are relative to the origin of the new
+     * (cropped) image. */
+    mask_offset_x -= x;
+    mask_offset_y -= y;
+
+    width = CLIP(0, width, mask->width + mask_offset_x);
+    height = CLIP(0, height, mask->height + mask_offset_y);
+
+    if (!gdk_pixbuf_get_has_alpha(mypixbuf)) {
+      GdkPixbuf *p2 = mypixbuf;
+
+      mypixbuf = gdk_pixbuf_add_alpha(mypixbuf, FALSE, 0, 0, 0);
+      g_object_unref(p2);
+    }
+
+    for (x1 = 0; x1 < width; x1++) {
+      for (y1 = 0; y1 < height; y1++) {
+       int mask_x = x1 - mask_offset_x, mask_y = y1 - mask_offset_y;
+       guchar *alpha = gdk_pixbuf_get_pixels(mypixbuf)
+         + y1 * gdk_pixbuf_get_rowstride(mypixbuf)
+         + x1 * gdk_pixbuf_get_n_channels(mypixbuf)
+         + 3;
+       guchar *mask_alpha = gdk_pixbuf_get_pixels(mask_pixbuf)
+         + mask_y * gdk_pixbuf_get_rowstride(mask_pixbuf)
+         + mask_x * gdk_pixbuf_get_n_channels(mask_pixbuf)
+         + 3;
+
+       *alpha = (*alpha) * (*mask_alpha) / 255;
+      }
+    }
+  }
+
+  return ctor_sprite(mypixbuf);
+}
+
+/****************************************************************************
+  Find the dimensions of the sprite.
+****************************************************************************/
+void get_sprite_dimensions(struct Sprite *sprite, int *width, int *height)
+{
+  *width = sprite->width;
+  *height = sprite->height;
+}
+
+/****************************************************************************
+  Create a new sprite with the given pixmap, dimensions, and
+  (optional) mask.
+
+  FIXME: should be renamed as sprite_new or some such.
+****************************************************************************/
+struct Sprite *ctor_sprite(GdkPixbuf *pixbuf)
+{
+  struct Sprite *sprite = fc_malloc(sizeof(*sprite));
+  bool has_alpha = FALSE, has_mask = FALSE;
+
+  sprite->width = gdk_pixbuf_get_width(pixbuf);
+  sprite->height = gdk_pixbuf_get_height(pixbuf);
+
+  /* Check to see if this pixbuf has an alpha layer. */
+  if (gdk_pixbuf_get_has_alpha(pixbuf)) {
+    guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
+    int x, y, rowstride = gdk_pixbuf_get_rowstride(pixbuf);
+
+    for (y = 0; y < sprite->height; y++) {
+      for (x = 0; x < sprite->width; x++) {
+       int i = y * rowstride + 4 * x + 3;
+       guchar pixel = pixels[i];
+
+       if (pixel > 0 && pixel < 255) {
+         has_alpha = TRUE;
+       }
+       if (pixel == 0) {
+         has_mask = TRUE;
+       }
+      }
+    }
+  }
+
+  sprite->pixbuf_fogged = NULL;
+  sprite->pixmap_fogged = NULL;
+  if (has_alpha) {
+    sprite->pixbuf = pixbuf;
+    sprite->pixmap = NULL;
+    sprite->mask = NULL;
+  } else {
+    gdk_pixbuf_render_pixmap_and_mask(pixbuf, &sprite->pixmap,
+                                     &sprite->mask, 1);
+    if (!has_mask && sprite->mask) {
+      g_object_unref(sprite->mask);
+      sprite->mask = NULL;
+    }
+    g_object_unref(pixbuf);
+    sprite->pixbuf = NULL;
+  }
+  return sprite;
+}
+
+/****************************************************************************
+  Returns the filename extensions the client supports
+  Order is important.
+****************************************************************************/
+const char **gfx_fileextensions(void)
+{
+  static const char *ext[] =
+  {
+    "png",
+    "xpm",
+    NULL
+  };
+
+  return ext;
+}
+
+/****************************************************************************
+  Load the given graphics file into a sprite.  This function loads an
+  entire image file, which may later be broken up into individual sprites
+  with crop_sprite.
+****************************************************************************/
+struct Sprite *load_gfxfile(const char *filename)
+{
+  GdkPixbuf *im;
+
+  if (!(im = gdk_pixbuf_new_from_file(filename, NULL))) {
+    freelog(LOG_FATAL, "Failed reading graphics file: %s", filename);
+    exit(EXIT_FAILURE);
+  }
+
+  return ctor_sprite(im);
+}
+
+/****************************************************************************
+  Free a sprite and all associated image data.
+****************************************************************************/
+void free_sprite(struct Sprite * s)
+{
+  if (s->pixmap) {
+    g_object_unref(s->pixmap);
+    s->pixmap = NULL;
+  }
+  if (s->mask) {
+    g_object_unref(s->mask);
+    s->mask = NULL;
+  }
+  if (s->pixbuf) {
+    g_object_unref(s->pixbuf);
+    s->pixbuf = NULL;
+  }
+  if (s->pixmap_fogged) {
+    g_object_unref(s->pixmap_fogged);
+  }
+  if (s->pixbuf_fogged) {
+    g_object_unref(s->pixbuf_fogged);
+  }
+  free(s);
+}
+
+/****************************************************************************
+  Scales a sprite. If the sprite contains a mask, the mask is scaled
+  as as well.
+****************************************************************************/
+struct Sprite *sprite_scale(struct Sprite *src, int new_w, int new_h)
+{
+  return ctor_sprite(gdk_pixbuf_scale_simple(sprite_get_pixbuf(src),
+                                            new_w, new_h,
+                                            GDK_INTERP_BILINEAR));
+}
+
+/****************************************************************************
+  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(struct Sprite * sprite, int *start_x,
+                            int *start_y, int *end_x, int *end_y)
+{
+  GdkImage *mask_image;
+  GdkBitmap *mask = sprite_get_mask(sprite);
+  int i, j;
+
+  if (!mask) {
+    *start_x = 0;
+    *start_y = 0;
+    *end_x = sprite->width - 1;
+    *end_y = sprite->height - 1;
+    return;
+  }
+
+  mask_image
+    = gdk_drawable_get_image(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;
+      }
+    }
+  }
+
+  g_object_unref(mask_image);
+}
+
+/****************************************************************************
+  Crops all blankspace from a sprite (insofar as is possible as a rectangle)
+****************************************************************************/
+struct Sprite *crop_blankspace(struct Sprite *s)
+{
+  int x1, y1, x2, y2;
+
+  sprite_get_bounding_box(s, &x1, &y1, &x2, &y2);
+
+  return crop_sprite(s, x1, y1, x2 - x1 + 1, y2 - y1 + 1, NULL, -1, -1);
+}
+
+/****************************************************************************
+  Converts a pixmap/mask sprite to a GdkPixbuf.
+
+  This is just a helper function for sprite_get_pixbuf().  Most callers
+  should use that function instead.
+****************************************************************************/
+static GdkPixbuf *gdk_pixbuf_new_from_pixmap_sprite(struct Sprite *src)
+{
+  GdkPixbuf *dst;
+  int w, h;
+
+  w = src->width;
+  h = src->height;
+  
+  /* convert pixmap */
+  dst = gdk_pixbuf_new(GDK_COLORSPACE_RGB, src->mask != NULL, 8, w, h);
+  gdk_pixbuf_get_from_drawable(dst, src->pixmap, NULL, 0, 0, 0, 0, w, h);
+
+  /* convert mask */
+  if (src->mask) {
+    GdkImage *img;
+    int x, y, rowstride;
+    guchar *pixels;
+
+    img = gdk_drawable_get_image(src->mask, 0, 0, w, h);
+
+    pixels = gdk_pixbuf_get_pixels(dst);
+    rowstride = gdk_pixbuf_get_rowstride(dst);
+
+    for (y = 0; y < h; y++) {
+      for (x = 0; x < w; x++) {
+       guchar *pixel = pixels + y * rowstride + x * 4 + 3;
+
+       if (gdk_image_get_pixel(img, x, y)) {
+         *pixel = 255;
+       } else {
+         *pixel = 0;
+       }
+      }
+    }
+    g_object_unref(img);
+  }
+
+  return dst;
+}
+
+/********************************************************************
+  Render a pixbuf from the sprite.
+
+  NOTE: the pixmap and mask of a sprite must not change after this
+        function is called!
+********************************************************************/
+GdkPixbuf *sprite_get_pixbuf(struct Sprite *sprite)
+{
+  if (!sprite) {
+    return NULL;
+  }
+  
+  if (!sprite->pixbuf) {
+    sprite->pixbuf = gdk_pixbuf_new_from_pixmap_sprite(sprite);
+  }
+  return sprite->pixbuf;
+}
+
+/****************************************************************************
+  Render a mask from the sprite.
+
+  NOTE: the pixbuf of a sprite must not change after this function is called!
+****************************************************************************/
+GdkBitmap *sprite_get_mask(struct Sprite *sprite)
+{
+  if (!sprite->pixmap && !sprite->mask) {
+    /* If we're not in pixmap mode and we don't yet have a mask, render
+     * the pixbuf to a mask. */
+    gdk_pixbuf_render_pixmap_and_mask(sprite->pixbuf, NULL,
+                                     &sprite->mask, 1);
+  }
+  return sprite->mask;
+}
Index: client/gui-gtk-2.0/sprite.h
===================================================================
RCS file: client/gui-gtk-2.0/sprite.h
diff -N client/gui-gtk-2.0/sprite.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ client/gui-gtk-2.0/sprite.h 22 Mar 2005 00:01:58 -0000
@@ -0,0 +1,50 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+#ifndef FC__SPRITE_H
+#define FC__SPRITE_H
+
+#include <gtk/gtk.h>
+
+#include "sprite_g.h"
+
+struct Sprite
+{
+  /* A pixmap + mask is used if there's a 1-bit alpha channel.  mask may be
+   * NULL if there's no alpha.  For multi-bit alpha levels, a pixbuf will be
+   * used instead.  For consistency a pixbuf may be generated on-demand when
+   * doing drawing (into a gtkpixcomm or gtkimage), so it's important that
+   * the sprite data not be changed after the sprite is loaded. */
+  GdkPixmap *pixmap, *pixmap_fogged;
+  GdkBitmap *mask;
+  GdkPixbuf *pixbuf, *pixbuf_fogged;
+
+  int       width;
+  int       height;
+};
+
+typedef struct Sprite SPRITE;
+
+struct Sprite *ctor_sprite(GdkPixbuf *pixbuf);
+struct Sprite *sprite_scale(struct Sprite *src, int new_w, int new_h);
+void sprite_get_bounding_box(struct Sprite *sprite, int *start_x,
+                            int *start_y, int *end_x, int *end_y);
+struct Sprite *crop_blankspace(struct Sprite *s);
+
+/********************************************************************
+  Note: a sprite cannot be changed after these functions are called!
+********************************************************************/
+GdkPixbuf *sprite_get_pixbuf(SPRITE *sprite);
+GdkBitmap *sprite_get_mask(struct Sprite *sprite);
+
+#endif  /* FC__SPRITE_H */
+
Index: client/gui-gtk-2.0/wldlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/wldlg.c,v
retrieving revision 1.42
diff -u -r1.42 wldlg.c
--- client/gui-gtk-2.0/wldlg.c  9 Mar 2005 20:43:48 -0000       1.42
+++ client/gui-gtk-2.0/wldlg.c  22 Mar 2005 00:01:59 -0000
@@ -24,6 +24,7 @@
 
 #include "city.h"
 #include "citydlg_common.h"
+#include "civclient.h"
 #include "fcintl.h"
 #include "game.h"
 #include "gui_main.h"
@@ -39,9 +40,10 @@
 #include "options.h"
 #include "tilespec.h"
 
-#include "wldlg.h"
+#include "canvas.h"
 #include "citydlg.h"
-#include "civclient.h"
+#include "graphics.h"
+#include "wldlg.h"
 
 static GtkWidget *worklists_shell;
 static GtkWidget *worklists_list;

[Prev in Thread] Current Thread [Next in Thread]