Complete.Org: Mailing Lists: Archives: freeciv-dev: November 2002:
[Freeciv-Dev] (PR#2269) PNG for freeciv
Home

[Freeciv-Dev] (PR#2269) PNG for freeciv

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients:;
Subject: [Freeciv-Dev] (PR#2269) PNG for freeciv
From: "Jason Short via RT" <rt@xxxxxxxxxxxxxx>
Date: Mon, 4 Nov 2002 03:04:06 -0800
Reply-to: rt@xxxxxxxxxxxxxx

Attached is an update of Vasco's patch for PNG under XAW.  See 
http://lists.complete.org/freeciv-dev@xxxxxxxxxxx/2001/10/msg00797.html.gz.

I've added minimal autoconf support, but we will need real support 
before we go further.  A patch would be welcome.

Now, this patch *replaces* XPM support with (limited) PNG support. 
There's nothing wrong with that (IMO), but it means when the patch is 
applied the xaw client will no longer be able to use XPMs.

MUI already supports PNG (in addition to XPM).  Win32 only supports PNG.

GTK2 support is easy; GdkPixbuf will load a PNG just as happily as an 
XPM (and faster).  But it has to know the name of the file - this just 
means adding a new entry to gfx_fileextensions.  Patch attached.  (When 
loading PNG graphics under this patch, the GTK2 client gives all sorts 
of warnings.  Vasco?)

GTK uses imlib - it works similarly (it even has the same warning messages).

Also (in no particular order), we need to upgrade the tilesets in CVS to 
PNG.  I have done a rough job with this, and put my results as png.tgz 
into incoming/.  But the tileset authors (Daniel, I'm looking at you) 
need to go through and make sure it's done right.

I suggest the following order of events:

- Apply the patches to GTK and GTK2 clients, so that they will work with 
PNG or XPM.

- Convert the graphics in CVS (trident, isotrident, and misc 
directories) to PNGs.

- Patch the XAW client to support PNG.

-----

A lot of places in the code refer to an image file as an XPM.  These 
should be sought out and fixed.  What is the correct terminology? 
Pixmap?  Image?  In any case, this can be done after the change is made.

-----

Imlib claims to support 25 different image formats.  I assume GdkPixbuf 
is comparably good.  On the one hand, it is tempting to put support for 
these formats into FreeCiv.  But doing so encourages them to be used, 
which is generally a bad thing.  If every FreeCiv client supports PNG, 
it is a bad idea to distribute a tileset in an format that only the GTK 
client can understand.  So for now I've just added PNG to the 
already-existing XPM support...

jason

? client/gui-stub/stub-update.diff
Index: configure.ac
===================================================================
RCS file: /home/freeciv/CVS/freeciv/configure.ac,v
retrieving revision 1.23
diff -u -r1.23 configure.ac
--- configure.ac        2002/10/11 23:45:46     1.23
+++ configure.ac        2002/11/04 10:29:25
@@ -351,6 +351,9 @@
     dnl Checks for X:
     AC_PATH_XTRA
 
+    # FIXME: check for libpng, and make sure we have libz
+    LDFLAGS="$LDFLAGS -lpng"
+
     dnl Determine the Xfuncproto control definitions:
     FC_CHECK_X_PROTO_DEFINE(FUNCPROTO)
     if test -n "$fc_x_proto_value"; then
Index: client//gui-xaw/graphics.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-xaw/graphics.c,v
retrieving revision 1.39
diff -u -r1.39 graphics.c
--- client//gui-xaw/graphics.c  2002/08/07 11:21:43     1.39
+++ client//gui-xaw/graphics.c  2002/11/04 10:29:26
@@ -17,15 +17,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
+#include <limits.h>
 
 #include <X11/Xlib.h>
 #include <X11/Intrinsic.h>
 
-#ifdef XPM_H_NO_X11
-#include <xpm.h>
-#else
-#include <X11/xpm.h>
-#endif
+#include <png.h>
 
 #include "fcintl.h"
 #include "game.h"
@@ -62,6 +60,7 @@
 Cursor nuke_cursor;
 Cursor patrol_cursor;
 
+static struct Sprite *ctor_sprite(Pixmap mypixmap, int width, int height);
 static struct Sprite *ctor_sprite_mask(Pixmap mypixmap, Pixmap mask, 
                                       int width, int height);
 
@@ -167,22 +166,27 @@
 struct Sprite *crop_sprite(struct Sprite *source,
                           int x, int y, int width, int height)
 {
-  GC plane_gc;
-  Pixmap mypixmap, mask;
+  Pixmap mypixmap;
 
   mypixmap = XCreatePixmap(display, root_window,
                           width, height, display_depth);
   XCopyArea(display, source->pixmap, mypixmap, civ_gc, 
            x, y, width, height, 0, 0);
 
-  mask = XCreatePixmap(display, root_window, width, height, 1);
-
-  plane_gc = XCreateGC(display, mask, 0, NULL);
-  XCopyArea(display, source->mask, mask, plane_gc, 
-           x, y, width, height, 0, 0);
-  XFreeGC(display, plane_gc);
-
-  return ctor_sprite_mask(mypixmap, mask, width, height);
+  if (source->has_mask) {
+    GC plane_gc;
+    Pixmap mask;
+
+    mask = XCreatePixmap(display, root_window, width, height, 1);
+
+    plane_gc = XCreateGC(display, mask, 0, NULL);
+    XCopyArea(display, source->mask, mask, plane_gc, 
+             x, y, width, height, 0, 0);
+    XFreeGC(display, plane_gc);
+    return ctor_sprite_mask(mypixmap, mask, width, height);
+  } else {
+    return ctor_sprite(mypixmap, width, height);
+  }
 }
 
 /***************************************************************************
@@ -257,7 +261,6 @@
   XFreePixmap(display, mask);
 }
 
-#ifdef UNUSED
 /***************************************************************************
 ...
 ***************************************************************************/
@@ -270,7 +273,6 @@
   mysprite->has_mask=0;
   return mysprite;
 }
-#endif
 
 /***************************************************************************
 ...
@@ -290,22 +292,6 @@
 
 
 
-#ifdef UNUSED
-/***************************************************************************
-...
-***************************************************************************/
-void dtor_sprite(struct Sprite *mysprite)
-{
-  XFreePixmap(display, mysprite->pixmap);
-  if(mysprite->has_mask)
-    XFreePixmap(display, mysprite->mask);
-  free_colors(mysprite->pcolorarray, mysprite->ncols);
-  free(mysprite->pcolorarray);
-  free(mysprite);
-
-}
-#endif
-
 /***************************************************************************
  Returns the filename extensions the client supports
  Order is important.
@@ -314,60 +300,226 @@
 {
   static char *ext[] =
   {
-    "xpm",
+    "png",
     NULL
   };
 
   return ext;
 }
 
+
+
+/***************************************************************************
+...
+***************************************************************************/
+static int is_transparent(png_byte index, png_bytep trans, int ntrans)
+{
+  int i;
+
+  for (i=0; i<ntrans; i++) {
+    if (index==trans[i])
+      return 1;
+  }
+  return 0;
+}
+
+/***************************************************************************
+...
+***************************************************************************/
+static Pixmap image2pixmap(XImage *xi)
+{
+  Pixmap ret;
+  XGCValues values;
+  GC gc;
+  
+  ret=XCreatePixmap(display, root_window, xi->width, xi->height, xi->depth);
+
+  values.foreground=1;
+  values.background=0;
+  gc=XCreateGC(display, ret, GCForeground|GCBackground, &values);
+
+  XPutImage(display, ret, gc, xi, 0, 0, 0, 0, xi->width, xi->height);
+  XFreeGC(display, gc);
+  return ret;
+}
+
 /***************************************************************************
 ...
 ***************************************************************************/
 struct Sprite *load_gfxfile(const char *filename)
 {
-  struct Sprite *mysprite;
-  Pixmap mypixmap, mask_bitmap;
-  int err;
-  XpmAttributes attributes;
+  png_structp pngp;
+  png_infop infop;
+  png_uint_32 sig_read=0;
+  png_int_32 width, height, row, col;
+  int bit_depth, color_type, interlace_type;
+  FILE *fp;
+
+  int npalette, ntrans;
+  png_colorp palette;
+  png_bytep trans;
+  unsigned long *pcolorarray;
+  png_bytep *row_pointers;
   
-  attributes.extensions = NULL;
-  attributes.valuemask = XpmCloseness|XpmColormap;
-  attributes.colormap = cmap;
-  attributes.closeness = 40000;
+  struct Sprite *mysprite;
+  XImage *xi;
+  int has_mask;
+
+  if (!(fp=fopen(filename, "rb"))) {
+    freelog(LOG_FATAL, _("Failed reading PNG file: %s"), filename);
+    exit(1);
+  }
+    
+  if (!(pngp=png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) 
{
+    freelog(LOG_FATAL, _("Failed creating PNG struct"));
+    exit(1);
+  }
 
-again:
+  if (!(infop=png_create_info_struct(pngp))) {
+    freelog(LOG_FATAL, _("Failed creating PNG struct"));
+    exit(1);
+  }
   
-  if((err=XpmReadFileToPixmap(display, root_window, (char*)filename, 
&mypixmap, 
-                             &mask_bitmap, &attributes))!=XpmSuccess) {
-    if(err==XpmColorError || err==XpmColorFailed) {
-      color_error();
-      goto again;
+  if (setjmp(pngp->jmpbuf)) {
+    freelog(LOG_FATAL, _("Failed while reading PNG file: %s"), filename);
+    exit(1);
+  }
+
+  png_init_io(pngp, fp);
+  png_set_sig_bytes(pngp, sig_read);
+
+  png_read_info(pngp, infop);
+  png_get_IHDR(pngp, infop, &width, &height, &bit_depth, &color_type,
+              &interlace_type, NULL, NULL);
+
+  png_set_strip_16(pngp);
+  png_set_packing(pngp);
+
+  if (png_get_PLTE(pngp, infop, &palette, &npalette)) {
+    int i;
+
+    pcolorarray=fc_malloc(npalette*sizeof(unsigned long));
+
+    for (i=0; i<npalette; i++) {
+      XColor mycolor;
+
+      mycolor.red  =palette[i].red;
+      mycolor.red  <<=8;
+      mycolor.green=palette[i].green;
+      mycolor.green<<=8;
+      mycolor.blue =palette[i].blue;
+      mycolor.blue <<=8;
+
+      if (XAllocColor(display, cmap, &mycolor))
+        pcolorarray[i]=mycolor.pixel;
+      else {
+        XColor *cols;
+        int ncols, j;
+
+        ncols=DefaultVisual(display, screen_number)->map_entries;
+        cols=fc_malloc(sizeof(XColor)*ncols);
+
+        for (j=0; j<ncols; j++)
+          cols[j].pixel=j;
+        XQueryColors(display, cmap, cols, ncols);
+
+        for (; i<npalette; i++) {
+         int best=INT_MAX;
+         unsigned long pixel=0;
+
+         for (j=0; j<ncols; j++) {
+           int rd, gd, bd, dist;
+           
+           rd=(cols[j].red  >>8)-palette[i].red;
+           gd=(cols[j].green>>8)-palette[i].green;
+           bd=(cols[j].blue >>8)-palette[i].blue;
+           dist=rd*rd+gd*gd+bd*bd;
+           
+           if (dist<best) {
+             best=dist; pixel=j;
+           }
+         }
+         pcolorarray[i]=pixel;
+        }
+       free(cols);
+        break;
+      }
     }
-    freelog(LOG_FATAL, _("Failed reading XPM file: %s"), filename);
+  } else {
+    freelog(LOG_FATAL, _("PNG file has no palette: %s"), filename);
     exit(EXIT_FAILURE);
   }
 
+  png_read_update_info(pngp, infop);
+  has_mask=png_get_tRNS(pngp, infop, &trans, &ntrans, NULL);
+
+  row_pointers=fc_malloc(sizeof(png_bytep)*height);
+
+  for (row=0; row<height; row++)
+    row_pointers[row]=fc_malloc(png_get_rowbytes(pngp, infop));
+
+  png_read_image(pngp, row_pointers);
+  png_read_end(pngp, infop);
+  fclose(fp);
+
+
   mysprite=fc_malloc(sizeof(struct Sprite));
+  mysprite->pixmap=0;
+  mysprite->mask=0;
   
-  mysprite->pixmap=mypixmap;
-  mysprite->mask=mask_bitmap;
-  mysprite->has_mask=(mask_bitmap!=0);
-  mysprite->width=attributes.width;
-  mysprite->height=attributes.height;
+  xi=XCreateImage(display, DefaultVisual(display, screen_number),
+                 display_depth, ZPixmap, 0, NULL, width, height, 32, 0);
+  xi->data=fc_calloc(xi->bytes_per_line*xi->height, 1);
+
+  for (row=0; row<height; row++) {
+    for (col=0; col<width; col++)
+      XPutPixel(xi, col, row, pcolorarray[row_pointers[row][col]]);
+  }
+  mysprite->pixmap=image2pixmap(xi);
+  XDestroyImage(xi);
+
+  if (has_mask) {
+    XImage *xm;
+
+    xm=XCreateImage(display, DefaultVisual(display, screen_number),
+                   1, XYBitmap, 0, NULL, width, height, 8, 0);
+    xm->data=fc_calloc(xm->bytes_per_line*xm->height, 1);
+
+    for (row=0; row<height; row++) {
+      for (col=0; col<width; col++) {
+       XPutPixel(xm, col, row,
+                 !is_transparent(row_pointers[row][col], trans, ntrans));
+      }
+    }
+    mysprite->mask=image2pixmap(xm);
+    XDestroyImage(xm);
+  }
 
+  mysprite->has_mask=has_mask;
+  mysprite->width=width;
+  mysprite->height=height;
+  mysprite->pcolorarray=pcolorarray;
+  mysprite->ncols=npalette;
+
+
+  for (row=0; row<height; row++)
+    free(row_pointers[row]);
+  free(row_pointers);
+  png_destroy_read_struct(&pngp, &infop, NULL);
   return mysprite;
 }
 
+
 /***************************************************************************
    Deletes a sprite.  These things can use a lot of memory.
-   
-   (How/why does this differ from dtor_sprite() ?  --dwp)
 ***************************************************************************/
 void free_sprite(struct Sprite *s)
 {
-  if(s->pixmap) XFreePixmap(display,s->pixmap);
-  if(s->has_mask) XFreePixmap(display,s->mask);
+  XFreePixmap(display, s->pixmap);
+  if(s->has_mask)
+    XFreePixmap(display, s->mask);
+/*  free_colors(s->pcolorarray, s->ncols);*/
+  free(s->pcolorarray);
   free(s);
 }
 
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.7
diff -u -r1.7 graphics.c
--- client/gui-gtk-2.0/graphics.c       2002/10/13 23:23:31     1.7
+++ client/gui-gtk-2.0/graphics.c       2002/11/04 11:02:47
@@ -302,6 +302,7 @@
 {
   static char *ext[] =
   {
+    "png",
     "xpm",
     NULL
   };
Index: client/gui-gtk/graphics.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/graphics.c,v
retrieving revision 1.43
diff -u -r1.43 graphics.c
--- client/gui-gtk/graphics.c   2002/09/28 03:33:09     1.43
+++ client/gui-gtk/graphics.c   2002/11/04 10:53:15
@@ -304,6 +304,7 @@
 {
   static char *ext[] =
   {
+    "png",
     "xpm",
     NULL
   };

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