Index: libgames-support/games-preimage.c
===================================================================
--- libgames-support/games-preimage.c	(révision 5946)
+++ libgames-support/games-preimage.c	(copie de travail)
@@ -1,5 +1,6 @@
 /* games-preimage.c
-   Copyright 2004 Richard Hoelscher
+   Copyright © 2004 Richard Hoelscher
+   Copyright © 2007 Christian Persch
    
    This library is free software; you can redistribute it and'or modify
    it under the terms of the GNU Library General Public License as published 
@@ -21,25 +22,34 @@
 
 #include "config.h"
 
+#include <math.h>
+#include <string.h>
+
 #include <glib.h>
 #include <glib/gi18n-lib.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
 
 #include "games-preimage.h"
 
-#define LOAD_BUFFER_SIZE 4096
+#define GET_PRIVATE(inst)(G_TYPE_INSTANCE_GET_PRIVATE ((inst), GAMES_TYPE_PREIMAGE, GamesPreimagePrivate))
+
+struct _GamesPreimagePrivate {
+  RsvgHandle *handle;
+};
 
 G_DEFINE_TYPE (GamesPreimage, games_preimage, G_TYPE_OBJECT);
 
 static void
 games_preimage_init (GamesPreimage * preimage)
 {
+  preimage->priv = GET_PRIVATE (preimage);
+
   preimage->scalable = FALSE;
-  preimage->srcsize = 0;
   preimage->width = 0;
   preimage->height = 0;
   preimage->pixbuf = NULL;
-  preimage->srcbuffer = NULL;
 }
 
 static void
@@ -47,8 +57,12 @@ games_preimage_finalize (GObject *object
 	
 {
   GamesPreimage *preimage = GAMES_PREIMAGE (object);
+  GamesPreimagePrivate *priv = preimage->priv;
+
+  if (priv->handle) {
+    g_object_unref (priv->handle);
+  }
 
-  g_free (preimage->srcbuffer);
   if (preimage->pixbuf != NULL)
     g_object_unref (preimage->pixbuf);
 
@@ -61,6 +75,10 @@ games_preimage_class_init (GamesPreimage
   GObjectClass *oclass = G_OBJECT_CLASS (klass);
 
   oclass->finalize = games_preimage_finalize;
+
+  g_type_class_add_private (oclass, sizeof (GamesPreimagePrivate));
+
+  rsvg_init ();
 }
 
 GamesPreimage *
@@ -69,28 +87,14 @@ games_preimage_new (void)
   return GAMES_PREIMAGE (g_object_new (GAMES_TYPE_PREIMAGE, NULL));
 }
 
-/* size_prepared_cb:
- * Handles the size-prepared signal in games_preimage_render */
-static void
-size_prepared_cb (GdkPixbufLoader * loader,
-		  gint width, gint height, gpointer data)
-{
-  struct {
-    gint width;
-    gint height;
-  } *info = data;
-
-  width = info->width;
-  height = info->height;
-  gdk_pixbuf_loader_set_size (loader, width, height);
-}
-
 /* games_preimage_render:
  * Create a GdkPixbuf from a GamesPreimage at the specified
  * width and height. */
 GdkPixbuf *
-games_preimage_render (GamesPreimage * preimage,
-		       gint width, gint height, GError ** error)
+games_preimage_render (GamesPreimage *preimage,
+		       gint width,
+                       gint height,
+                       GError **error)
 {
   GdkPixbuf *pixbuf;
 
@@ -98,54 +102,120 @@ games_preimage_render (GamesPreimage * p
   g_return_val_if_fail (preimage != NULL, NULL);
 
   if (preimage->scalable) {	/* Render vector image */
+    pixbuf = games_preimage_render_sub (preimage,
+                                        NULL,
+                                        width,
+                                        height,
+                                        0.0, 0.0,
+                                        ((double) width) / ((double) preimage->width),
+                                        ((double) height) / ((double) preimage->height),
+                                        error);
+  } else {			/* Render raster image */
 
-    GdkPixbufLoader *loader;
+    pixbuf = gdk_pixbuf_scale_simple (preimage->pixbuf,
+				      width, height, GDK_INTERP_BILINEAR);
+  }
 
-    struct {
-      gint width;
-      gint height;
-    } info;
+  return pixbuf;
+}
 
-    guchar *buffer = preimage->srcbuffer;
-    gsize buffer_size = preimage->srcsize;
+/* This routine is copied from librsvg:
+   Copyright © 2005 Dom Lachowicz <cinamod@hotmail.com>
+   Copyright © 2005 Caleb Moore <c.moore@student.unsw.edu.au>
+   Copyright © 2005 Red Hat, Inc.
+ */
+static void
+cairo_pixels_to_pixbuf (guint8 *pixels,
+                        int rowstride,
+                        int height)
+{
+  int row;
+  /* un-premultiply data */
+  for(row = 0; row < height; row++) {
+    guint8 *row_data = (pixels + (row * rowstride));
+    int i;
 
-    info.width = width;
-    info.height = height;
+    for(i = 0; i < rowstride; i += 4) {
+      guint8 *b = &row_data[i];
+      guint32 pixel;
+      guint8 alpha;
 
-    loader = gdk_pixbuf_loader_new ();
+      memcpy(&pixel, b, sizeof(guint32));
+      alpha = (pixel & 0xff000000) >> 24;
+      if(alpha == 0) {
+        b[0] = b[1] = b[2] = b[3] = 0;
+      } else {
+        b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+        b[1] = (((pixel & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
+        b[2] = (((pixel & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
+        b[3] = alpha;
+      }
+    }
+  }
+}
 
-    g_signal_connect (loader, "size-prepared",
-		      G_CALLBACK (size_prepared_cb), &info);
+GdkPixbuf *
+games_preimage_render_sub (GamesPreimage *preimage,
+                           const char *node,
+                           int width,
+                           int height,
+                           double xoffset,
+                           double yoffset,
+                           double xzoom,
+                           double yzoom,
+                           GError **error)
+{
+  GamesPreimagePrivate *priv = preimage->priv;
+  GdkPixbuf *pixbuf = NULL;
+  int rowstride;
+  guint8 *data;
+  cairo_surface_t *surface;
+  cairo_t *cx;
 
-    if (!gdk_pixbuf_loader_write (loader, buffer, buffer_size, error)) {
-      gdk_pixbuf_loader_close (loader, NULL);
-      g_object_unref (loader);
-      return NULL;
-    }
+  if (!preimage->scalable)
+    return NULL;
 
-    if (!gdk_pixbuf_loader_close (loader, error)) {
-      g_object_unref (loader);
-      return NULL;
-    }
+  rowstride = width * 4;
 
-    pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+  data = g_try_malloc0 (rowstride * height);
+  if (!data)
+    return NULL;
 
-    if (!pixbuf) {
-      g_object_unref (loader);
-      g_set_error (error,
-		   GDK_PIXBUF_ERROR,
-		   GDK_PIXBUF_ERROR_FAILED, _("Image rendering failed."));
-      return NULL;
-    }
+  surface = cairo_image_surface_create_for_data (data,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 width, height, rowstride);
+  if (!surface || cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
+    return NULL;
 
-    g_object_ref (pixbuf);
-    g_object_unref (loader);
+  cx = cairo_create (surface);
+  if (!cx || cairo_status (cx) != CAIRO_STATUS_SUCCESS)
+    goto error;
 
-  } else {			/* Render raster image */
+  cairo_matrix_t matrix;
+  cairo_matrix_init_identity (&matrix);
+  cairo_matrix_scale (&matrix, xzoom, yzoom);
+  cairo_matrix_translate (&matrix, xoffset, yoffset);
 
-    pixbuf = gdk_pixbuf_scale_simple (preimage->pixbuf,
-				      width, height, GDK_INTERP_BILINEAR);
-  }
+  cairo_set_matrix (cx, &matrix);
+
+  rsvg_handle_render_cairo_sub (priv->handle, cx, node);
+
+  cairo_pixels_to_pixbuf (data, rowstride, height);
+
+  pixbuf = gdk_pixbuf_new_from_data (data,
+                                     GDK_COLORSPACE_RGB,
+                                     TRUE,
+                                     8,
+                                     width, height,
+                                     rowstride,
+                                     (GdkPixbufDestroyNotify) g_free,
+                                     NULL);
+ 
+  cairo_destroy (cx);
+
+error:
+
+  cairo_surface_destroy (surface);
 
   return pixbuf;
 }
@@ -157,57 +227,46 @@ GamesPreimage *
 games_preimage_new_from_file (const gchar * filename, GError ** error)
 {
   GamesPreimage *preimage;
-  GdkPixbuf *pixbuf;
-  GdkPixbufFormat *format;
-
-  struct {
-    gint width;
-    gint height;
-    gboolean scalable;
-  } info;
+  GamesPreimagePrivate *priv;
+  GError *err = NULL;
 
   g_return_val_if_fail (filename != NULL, NULL);
 
-  format = gdk_pixbuf_get_file_info (filename, &info.width, &info.height);
-
-  /* Catch a dud filename. This is not a warning since we might be 
-   * blindly, but deliberately, testing for files. */
-  if (!format) {
-    return NULL;
-  }
+  preimage = games_preimage_new ();
+  priv = preimage->priv;
 
-  info.scalable = gdk_pixbuf_format_is_scalable (format);
+  priv->handle = rsvg_handle_new_from_file (filename, &err);
+  if (priv->handle) {
+    RsvgDimensionData data;
 
-  if (info.scalable) {		/* Prepare a vector image... */
+    preimage->scalable = TRUE;
 
-    preimage = games_preimage_new ();
+    rsvg_handle_get_dimensions (priv->handle, &data);
 
-    preimage->scalable = info.scalable;
-    preimage->width = info.width;
-    preimage->height = info.height;
+    preimage->width = data.width;
+    preimage->height = data.height;
 
-    if (!g_file_get_contents (filename,
-			      (gchar **) (char *) &(preimage->srcbuffer),
-			      &(preimage->srcsize), error)) {
+    if (data.width == 0 || data.height == 0) {
+      g_set_error (error,
+                   GDK_PIXBUF_ERROR,
+                   GDK_PIXBUF_ERROR_FAILED, _("Image has zero extent"));
       g_object_unref (preimage);
       return NULL;
     }
+  } else {
+    GdkPixbuf *pixbuf;
 
-  } else {			/* ...Or prepare a raster image */
-    pixbuf = gdk_pixbuf_new_from_file (filename, error);
+    /* Not an SVG */
+    preimage->scalable = FALSE;
 
+    pixbuf = gdk_pixbuf_new_from_file (filename, error);
     if (!pixbuf) {
       return NULL;
     }
 
-    g_object_ref (pixbuf);
-
-    preimage = games_preimage_new ();
-
-    preimage->scalable = info.scalable;
-    preimage->pixbuf = pixbuf;
-    preimage->width = info.width;
-    preimage->height = info.height;
+    preimage->pixbuf = g_object_ref (pixbuf);
+    preimage->width = gdk_pixbuf_get_width (pixbuf);
+    preimage->height = gdk_pixbuf_get_height (pixbuf);
   }
 
   return preimage;
@@ -217,7 +276,7 @@ gboolean
 games_preimage_is_scalable (GamesPreimage * preimage)
 {
   g_return_val_if_fail (preimage != NULL, FALSE);
-  return preimage->scalable != FALSE;
+  return preimage->scalable;
 }
 
 gint
@@ -244,8 +303,10 @@ games_preimage_render_unscaled_pixbuf (G
   if ((unscaled_pixbuf = preimage->pixbuf)) {
     g_object_ref (unscaled_pixbuf);
   } else {
-    unscaled_pixbuf = games_preimage_render (preimage, preimage->width,
-					     preimage->height, NULL);
+    unscaled_pixbuf = games_preimage_render (preimage,
+                                             preimage->width,
+					     preimage->height,
+                                             NULL);
   }
 
   return unscaled_pixbuf;
Index: libgames-support/games-preimage.h
===================================================================
--- libgames-support/games-preimage.h	(révision 5946)
+++ libgames-support/games-preimage.h	(copie de travail)
@@ -32,17 +32,20 @@ G_BEGIN_DECLS
 #define GAMES_IS_PREIMAGE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAMES_TYPE_PREIMAGE))
 #define GAMES_IS_PREIMAGE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GAMES_TYPE_PREIMAGE))
 #define GAMES_GET_PREIMAGE_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GAMES_TYPE_PREIMAGE, GamesPreimageClass))
-  typedef struct _GamesPreimage {
+
+typedef struct _GamesPreimagePrivate GamesPreimagePrivate;
+
+typedef struct _GamesPreimage {
   GObject parent;
 
-  gboolean scalable;
+  GamesPreimagePrivate *priv;
+
   gint width;
   gint height;
-  /* scalable source data */
-  guchar *srcbuffer;
-  gsize srcsize;
+
   /* raster pixbuf data */
   GdkPixbuf *pixbuf;
+  guint scalable : 1;
 } GamesPreimage;
 
 typedef struct _GamesPreimageClass {
@@ -59,6 +62,16 @@ GamesPreimage *games_preimage_new_from_f
 GdkPixbuf *games_preimage_render (GamesPreimage * preimage,
 				  gint width, gint height, GError ** error);
 
+GdkPixbuf *games_preimage_render_sub (GamesPreimage *preimage,
+                                      const char *node,
+                                      int width,
+                                      int height,
+                                      double xoffset,
+                                      double yoffset,
+                                      double xzoom,
+                                      double yzoom,
+                                      GError **error);
+
 gboolean games_preimage_is_scalable (GamesPreimage * preimage);
 
 gint games_preimage_get_width (GamesPreimage * preimage);
Index: libgames-support/games-card-images.c
===================================================================
--- libgames-support/games-card-images.c	(révision 5946)
+++ libgames-support/games-card-images.c	(copie de travail)
@@ -1,5 +1,6 @@
 /* games-card-images.c
-   Copyright 2004 Callum McKenzie
+   Copyright © 2004 Callum McKenzie
+   Copyright © 2007 Christian Persch
 
    This library is free software; you can redistribute it and'or modify
    it under the terms of the GNU Library General Public License as published 
@@ -22,6 +23,7 @@
 /* #defining this prints out the time it takes to run 
  * games_preimage_new_from_file (in seconds). */
 #undef INSTRUMENT_LOADING
+#define INSTRUMENT_LOADING
 
 #include <glib.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
@@ -31,6 +33,45 @@
 #include "games-find-file.h"
 #include "games-preimage.h"
 
+static const char extra_cards[][12] = {
+  "black_joker",
+  "red_joker",
+  "back"
+};
+
+static const char suites[][8] = {
+  "club",
+  "diamond",
+  "heart",
+  "spade"
+};
+
+static const char ranks[][6] = {
+  "1",
+  "2",
+  "3",
+  "4",
+  "5",
+  "6",
+  "7",
+  "8",
+  "9",
+  "10",
+  "jack",
+  "queen",
+  "king"
+};
+
+#ifdef INSTRUMENT_LOADING
+static long totaltime = 0;
+#endif
+
+#define ROWS ((double) 5)
+#define COLS ((double) 13)
+
+#define FLOAT_TO_INT_CEIL(f) ((int) (f + 0.5f))
+#define DELTA (0.0f)
+
 G_DEFINE_TYPE (GamesCardImages, games_card_images, G_TYPE_OBJECT);
 
 GamesCardImages *
@@ -42,27 +83,66 @@ games_card_images_new (void)
 static void
 games_card_images_render (GamesCardImages * images, gint cardid)
 {
-  int sx, sy;
+  GamesPreimage *preimage = images->preimage;
   GdkPixbuf *subpixbuf;
+  int suit, rank;
 
-  sx = cardid % 13;
-  sy = cardid / 13;
+  rank = cardid % 13;
+  suit = cardid / 13;
 
-  subpixbuf = gdk_pixbuf_new_subpixbuf (images->source, sx * images->subwidth,
-					sy * images->subheight,
+  if (preimage->scalable && images->subrender) {
+    double card_width, card_height;
+    double width, height;
+    double offsetx, offsety;
+    double zoomx, zoomy;
+    char node[64];
+
+    card_width = preimage->width / COLS;
+    card_height = preimage->height / ROWS;
+
+    width = images->width - 2 * DELTA;
+    height = images->height - 2 * DELTA;
+
+    offsetx = - ((double) rank) * card_width + DELTA;
+    offsety = - ((double) suit) * card_height + DELTA;
+
+    zoomx = width / card_width;
+    zoomy = height / card_height;
+
+    if (suit < 4) {
+      g_snprintf (node, sizeof (node), "#%s_%s", ranks[rank], suites[suit]);
+    } else {
+      g_snprintf (node, sizeof (node), "#%s", extra_cards[rank]);
+    }
+
+    subpixbuf = games_preimage_render_sub (preimage,
+                                           node,
+                                           images->width, images->height,
+                                           offsetx, offsety,
+                                           zoomx, zoomy,
+                                           NULL);
+    if (!subpixbuf) {
+      return;
+    }
+                                         
+    images->pixbufs[cardid] = subpixbuf;
+
+    return;
+  }
+
+  subpixbuf = gdk_pixbuf_new_subpixbuf (images->source,
+                                        rank * images->subwidth,
+					suit * images->subheight,
 					images->subwidth, images->subheight);
   if (images->prescaled) {
     images->pixbufs[cardid] = subpixbuf;
-    g_object_ref (images->pixbufs[cardid]);
   } else {
     images->pixbufs[cardid] = gdk_pixbuf_scale_simple (subpixbuf,
 						       images->width,
 						       images->height,
 						       GDK_INTERP_BILINEAR);
+    g_object_unref (subpixbuf);
   }
-
-  images->rendered[cardid] = TRUE;
-  g_object_unref (subpixbuf);
 }
 
 static void
@@ -104,26 +184,31 @@ games_card_images_prerender (GamesCardIm
   if (!images->preimage) {
     /* FIXME: Find a better way to report errors. */
     g_warning ("Failed to load the fallback file.\n");
+    return;
   }
 
   images->prescaled = games_preimage_is_scalable (images->preimage);
 
   if (!images->prescaled) {
     images->source = games_preimage_render_unscaled_pixbuf (images->preimage);
-  } else {
+  } else if (!images->subrender) {
     images->source = games_preimage_render (images->preimage,
 					    images->width * 13,
 					    images->height * 5, NULL);
   }
 
-  images->subwidth = gdk_pixbuf_get_width (images->source) / 13;
-  images->subheight = gdk_pixbuf_get_height (images->source) / 5;
+  if (images->source) {
+    images->subwidth = gdk_pixbuf_get_width (images->source) / 13;
+    images->subheight = gdk_pixbuf_get_height (images->source) / 5;
+  }
 
   images->prerendered = TRUE;
 
 #ifdef INSTRUMENT_LOADING
   t2 = clock ();
-  g_print ("%f\n", (t2 - t1) * 1.0 / CLOCKS_PER_SEC);
+  totaltime += (t2-t1);
+  g_print ("took %.3fs to prerender (cumulative %.3fs)\n", (t2 - t1) * 1.0 / CLOCKS_PER_SEC,
+           totaltime * 1.0 / CLOCKS_PER_SEC);
 #endif
 }
 
@@ -132,16 +217,19 @@ games_card_images_purge (GamesCardImages
 {
   int i;
 
-  if (images->prerendered)
+  if (images->source)
     g_object_unref (images->source);
+  images->source = NULL;
 
   for (i = 0; i < GAMES_CARDS_TOTAL; i++) {
-    if (images->rendered[i])
+    if (images->pixbufs[i])
       g_object_unref (images->pixbufs[i]);
-    images->rendered[i] = FALSE;
+    images->pixbufs[i] = NULL;
   }
 
   images->prerendered = FALSE;
+
+  totaltime = 0;
 }
 
 GdkPixbuf *
@@ -152,9 +240,22 @@ games_card_images_get_card_by_id (GamesC
   if (!images->prerendered)
     games_card_images_prerender (images);
 
-  if (!images->rendered[cardid])
+#ifdef INSTRUMENT_LOADING
+  clock_t t1, t2;
+
+  t1 = clock ();
+#endif
+
+  if (!images->pixbufs[cardid])
     games_card_images_render (images, cardid);
 
+#ifdef INSTRUMENT_LOADING
+  t2 = clock ();
+  totaltime += (t2-t1);
+  g_print ("took %.3fs to render card %d (cumulative: %.3fs)\n", (t2 - t1) * 1.0 / CLOCKS_PER_SEC, cardid,
+           totaltime * 1.0 / CLOCKS_PER_SEC);
+#endif
+
   return images->pixbufs[cardid];
 }
 
@@ -201,7 +302,7 @@ games_card_images_set_size (GamesCardIma
 }
 
 void
-games_card_images_set_theme (GamesCardImages * images, gchar * name)
+games_card_images_set_theme (GamesCardImages * images, const gchar * name)
 {
   /* Ignore the two obvious problem cases silently. */
   if ((!name) || (*name == '\0'))
@@ -250,6 +351,11 @@ games_card_images_init (GamesCardImages 
   cardimages->prescaled = FALSE;
 
   cardimages->prerendered = FALSE;
-  cardimages->rendered = g_new0 (gboolean, GAMES_CARDS_TOTAL);
   cardimages->pixbufs = g_new0 (GdkPixbuf *, GAMES_CARDS_TOTAL);
+
+  const char *env = g_getenv ("GAMES_CARDS_RENDER_SUB");
+  if (env && g_ascii_strtoull (env, NULL, 0) != 0) {
+    g_print ("Using subrendering\n");
+    cardimages->subrender = TRUE;
+  }
 }
Index: libgames-support/games-card-images.h
===================================================================
--- libgames-support/games-card-images.h	(révision 5946)
+++ libgames-support/games-card-images.h	(copie de travail)
@@ -26,7 +26,8 @@ G_BEGIN_DECLS
 #include <glib.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include "games-preimage.h"
-  enum {
+  
+enum {
   GAMES_CARD_ACE = 1,
   GAMES_CARD_TWO = 2,
   GAMES_CARD_THREE = 3,
@@ -63,9 +64,9 @@ typedef struct _GamesCardImages {
 
   GdkPixbuf *source;
   gint subwidth, subheight;
-  gboolean *rendered;
   GdkPixbuf **pixbufs;
 
+  guint subrender : 1;
   guint prescaled : 1; /* preimage */
   guint prerendered : 1;
 } GamesCardImages;
@@ -100,7 +101,7 @@ GdkPixbuf *games_card_images_get_back (G
 
 void games_card_images_set_size (GamesCardImages * images,
 				 gint width, gint height);
-void games_card_images_set_theme (GamesCardImages * images, gchar * name);
+void games_card_images_set_theme (GamesCardImages * images, const gchar * name);
 
 G_END_DECLS
 #endif /* GAMES_CARD_IMAGES_H */
