[gnome-games] aisleriot: Fix build with gtk3



commit 7ad7aad4652fd286a0a2f15d67258cc96092bb9c
Author: Christian Persch <chpe gnome org>
Date:   Thu Aug 12 12:59:43 2010 +0200

    aisleriot: Fix build with gtk3
    
    Finish cairo drawing code.

 aisleriot/ar-cursor.c                 |   84 ++++++++-
 aisleriot/board-noclutter.c           |  309 ++++++++++++++++++-----------
 aisleriot/lib/Makefile.am             |   15 ++-
 aisleriot/lib/ar-card-surface-cache.c |  349 +++++++++++++++++++++++++++++++++
 aisleriot/lib/ar-card-surface-cache.h |   74 +++++++
 aisleriot/lib/ar-card-theme-private.h |    2 +
 aisleriot/lib/ar-card-theme.c         |   60 ++++++
 aisleriot/lib/ar-card-theme.h         |    7 +-
 configure.in                          |    3 +
 libgames-support/games-preimage.c     |  111 +++++++----
 libgames-support/games-preimage.h     |   10 +
 11 files changed, 865 insertions(+), 159 deletions(-)
---
diff --git a/aisleriot/ar-cursor.c b/aisleriot/ar-cursor.c
index 00f7e49..b10ae98 100644
--- a/aisleriot/ar-cursor.c
+++ b/aisleriot/ar-cursor.c
@@ -20,10 +20,57 @@
 
 #include "ar-cursor.h"
 
+#include <gtk/gtk.h>
+
 #ifndef HAVE_HILDON
 
 /* These cursors borrowed from EOG */
 /* FIXMEchpe use themeable cursors here! */
+
+#if GTK_CHECK_VERSION (2, 90, 6)
+
+#define hand_closed_data_width 20
+#define hand_closed_data_height 20
+static const char hand_closed_data_bits[] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
+  0x80, 0xff, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0xb0, 0xff, 0x00, 0x00, 0xf0, 0xff, 0x00, 0x00,
+  0xe0, 0xff, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
+  0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define hand_closed_mask_width 20
+#define hand_closed_mask_height 20
+static const char hand_closed_mask_bits[] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00,
+  0xc0, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x01, 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf8, 0xff, 0x01, 0x00,
+  0xf0, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00,
+  0x80, 0x7f, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define hand_open_data_width 20
+#define hand_open_data_height 20
+static const char hand_open_data_bits[] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+  0x60, 0x36, 0x00, 0x00, 0x60, 0x36, 0x00, 0x00, 0xc0, 0x36, 0x01, 0x00, 0xc0, 0xb6, 0x01, 0x00,
+  0x80, 0xbf, 0x01, 0x00, 0x98, 0xff, 0x01, 0x00, 0xb8, 0xff, 0x00, 0x00, 0xf0, 0xff, 0x00, 0x00,
+  0xe0, 0xff, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
+  0x00, 0x3f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+#define hand_open_mask_width 20
+#define hand_open_mask_height 20
+static const char hand_open_mask_bits[] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x60, 0x3f, 0x00, 0x00,
+  0xf0, 0x7f, 0x00, 0x00, 0xf0, 0x7f, 0x01, 0x00, 0xe0, 0xff, 0x03, 0x00, 0xe0, 0xff, 0x03, 0x00,
+  0xd8, 0xff, 0x03, 0x00, 0xfc, 0xff, 0x03, 0x00, 0xfc, 0xff, 0x01, 0x00, 0xf8, 0xff, 0x01, 0x00,
+  0xf0, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00,
+  0x80, 0x7f, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#else
+
 #define hand_closed_data_width 20
 #define hand_closed_data_height 20
 static const char hand_closed_data_bits[] = {
@@ -64,6 +111,8 @@ static const char hand_open_mask_bits[] = {
   0x80, 0x7f, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
+#endif /* GTK 2.90.6 */
+
 static GdkCursor *
 ar_cursor_new_from_data (GdkWindow *window,
                          const char *data,
@@ -71,14 +120,47 @@ ar_cursor_new_from_data (GdkWindow *window,
 {
   const GdkColor fg = { 0, 65535, 65535, 65535 };
   const GdkColor bg = { 0, 0, 0, 0 };
+  GdkCursor *cursor;
   GdkPixmap *source;
   GdkPixmap *mask;
-  GdkCursor *cursor;
+#if GTK_CHECK_VERSION (2, 90, 6)
+  cairo_surface_t *image;
+  cairo_t *cr;
+
+  /* Yeah, hard-coded sizes are bad. */
+  source = gdk_pixmap_new (window, 20, 20, 1);
+
+  cr = gdk_cairo_create (source );
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  image = cairo_image_surface_create_for_data ((guchar *) data,
+                                                CAIRO_FORMAT_A1,
+                                                20, 20, 4);
+  cairo_set_source_surface (cr, image, 0, 0);
+  cairo_surface_destroy (image);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+  /* Yeah, hard-coded sizes are bad. */
+  mask = gdk_pixmap_new (window, 20, 20, 1);
+
+  cr = gdk_cairo_create (mask);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  image = cairo_image_surface_create_for_data ((guchar *) mask_data,
+                                                CAIRO_FORMAT_A1,
+                                                20, 20, 4);
+  cairo_set_source_surface (cr, image, 0, 0);
+  cairo_surface_destroy (image);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+#else
 
   /* Yeah, hard-coded sizes are bad. */
   source = gdk_bitmap_create_from_data (window, data, 20, 20);
   mask = gdk_bitmap_create_from_data (window, mask_data, 20, 20);
 
+#endif /* GTK 2.90.6 */
+
   cursor = gdk_cursor_new_from_pixmap (source, mask, &fg, &bg, 10, 10);
 
   g_object_unref (source);
diff --git a/aisleriot/board-noclutter.c b/aisleriot/board-noclutter.c
index cbff1b7..53c426b 100644
--- a/aisleriot/board-noclutter.c
+++ b/aisleriot/board-noclutter.c
@@ -28,6 +28,10 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 
+#if GTK_CHECK_VERSION (2, 90, 6)
+#define CAIRO_DRAWING
+#endif
+
 #include <libgames-support/games-files.h>
 #include <libgames-support/games-glib-compat.h>
 #include <libgames-support/games-gtk-compat.h>
@@ -37,13 +41,14 @@
 
 #include "conf.h"
 #include "game.h"
-#include "ar-card-images.h"
 #include "ar-cursor.h"
 #include "ar-pixbuf-utils.h"
 #include "ar-style-gtk.h"
 
-#if GTK_CHECK_VERSION (2, 90, 6)
-#define CAIRO_DRAWING
+#ifdef CAIRO_DRAWING
+#include "ar-card-surface-cache.h"
+#else
+#include "ar-card-images.h"
 #endif
 
 #define AISLERIOT_BOARD_GET_PRIVATE(board)(G_TYPE_INSTANCE_GET_PRIVATE ((board), AISLERIOT_TYPE_BOARD, AisleriotBoardPrivate))
@@ -70,6 +75,9 @@
 #define PIXBUF_DRAWING_LIKELIHOOD(cond) (cond)
 #endif /* HAVE_HILDON */
 
+/* FIXMEchpe */
+#define HIGHLIGHT_ALPHA (0.5)
+
 #define I_(string) g_intern_static_string (string)
 
 typedef enum {
@@ -116,10 +124,16 @@ struct _AisleriotBoardPrivate
   int xbaseoffset;
 
   /* Cards cache */
+#ifdef CAIRO_DRAWING
+  ArCardSurfaceCache *card_cache;
+  cairo_surface_t *slot_surface;
+#else
   ArCardImages *images;
 
   /* ArSlot */
   gpointer slot_image; /* either a GdkPixbuf or GdkPixmap, depending on drawing mode */
+#endif
+
 
   /* Button press */
   int last_click_x;
@@ -721,10 +735,14 @@ slot_update_card_images_full (AisleriotBoard *board,
                               int highlight_start_card_id)
 {
   AisleriotBoardPrivate *priv = board->priv;
-  ArCardImages *images = priv->images;
   GPtrArray *card_images;
   guint n_cards, first_exposed_card_id, i;
   guint8 *cards;
+#ifdef CAIRO_DRAWING
+  ArCardSurfaceCache *card_cache = priv->card_cache;
+#else
+  ArCardImages *images = priv->images;
+#endif
 
   card_images = slot->card_images;
   g_ptr_array_set_size (card_images, 0);
@@ -745,9 +763,15 @@ slot_update_card_images_full (AisleriotBoard *board,
     g_ptr_array_add (card_images, NULL);
   }
 
-#ifndef CAIRO_DRAWING
+#ifdef CAIRO_DRAWING
+  for (i = first_exposed_card_id; i < n_cards; ++i) {
+    Card card = CARD (cards[i]);
+
+    g_ptr_array_add (card_images,
+                     ar_card_surface_cache_get_card_surface (card_cache, card));
+  }
+#else
   if (PIXBUF_DRAWING_LIKELIHOOD (priv->use_pixbuf_drawing)) {
-#endif
     for (i = first_exposed_card_id; i < n_cards; ++i) {
       Card card = CARD (cards[i]);
       gboolean is_highlighted;
@@ -757,7 +781,6 @@ slot_update_card_images_full (AisleriotBoard *board,
       g_ptr_array_add (card_images,
                        ar_card_images_get_card_pixbuf (images, card, is_highlighted));
     }
-#ifndef CAIRO_DRAWING
   } else {
     for (i = first_exposed_card_id; i < n_cards; ++i) {
       Card card = CARD (cards[i]);
@@ -769,7 +792,7 @@ slot_update_card_images_full (AisleriotBoard *board,
                        ar_card_images_get_card_pixmap (images, card, is_highlighted));
     }
   }
-#endif /* !CAIRO_DRAWING */
+#endif /* CAIRO_DRAWING */
 }
 
 static void
@@ -811,6 +834,9 @@ aisleriot_board_setup_geometry (AisleriotBoard *board)
   guint i, n_slots;
   CardSize card_size;
   gboolean size_changed;
+#ifdef CAIRO_DRAWING
+  ArCardTheme *theme;
+#endif
 
   /* Nothing to do yet */
   if (aisleriot_game_get_state (priv->game) <= GAME_LOADED)
@@ -822,12 +848,24 @@ aisleriot_board_setup_geometry (AisleriotBoard *board)
   priv->xslotstep = ((double) priv->allocation.width) / priv->width;
   priv->yslotstep = ((double) priv->allocation.height) / priv->height;
 
-  size_changed = ar_card_images_set_size (priv->images,
-                                             priv->xslotstep,
-                                             priv->yslotstep,
-                                             priv->card_slot_ratio);
+#ifdef CAIRO_DRAWING
+  theme = ar_style_get_card_theme (priv->style);
+  if (theme == NULL)
+    return;
 
+  size_changed = ar_card_theme_set_size (theme,
+                                         priv->xslotstep,
+                                         priv->yslotstep,
+                                         priv->card_slot_ratio);
+  ar_card_theme_get_size (theme, &card_size);
+#else
+  size_changed = ar_card_images_set_size (priv->images,
+                                          priv->xslotstep,
+                                          priv->yslotstep,
+                                          priv->card_slot_ratio);
   ar_card_images_get_size (priv->images, &card_size);
+#endif
+
   priv->card_size = card_size;
 
   /* If the cards are too far apart, bunch them in the middle. */
@@ -847,18 +885,18 @@ aisleriot_board_setup_geometry (AisleriotBoard *board)
   priv->xoffset = (priv->xslotstep - card_size.width) / 2;
   priv->yoffset = (priv->yslotstep - card_size.height) / 2;
 
-#ifndef CAIRO_DRAWING
+#ifdef CAIRO_DRAWING
+  priv->slot_surface = ar_card_surface_cache_get_slot_surface (priv->card_cache);
+#else
   if (PIXBUF_DRAWING_LIKELIHOOD (priv->use_pixbuf_drawing)) {
-#endif
     priv->slot_image = ar_card_images_get_slot_pixbuf (priv->images, FALSE);
-#ifndef CAIRO_DRAWING
   } else {
     priv->slot_image = ar_card_images_get_slot_pixmap (priv->images, FALSE);
   }
 
   gdk_gc_set_clip_mask (priv->slot_gc, ar_card_images_get_slot_mask (priv->images));
   gdk_gc_set_clip_mask (priv->draw_gc, ar_card_images_get_card_mask (priv->images));
-#endif /* !CAIRO_DRAWING */
+#endif /* CAIRO_DRAWING */
 
   /* NOTE! Updating the slots checks that geometry is set, so
    * we set it to TRUE already.
@@ -890,8 +928,7 @@ drag_begin (AisleriotBoard *board)
   int delta, height, width;
   int x, y;
   GdkPixmap *moving_pixmap;
-  GdkBitmap *card_mask;
-  GdkBitmap *moving_mask;
+  GdkPixmap *moving_mask;
   int num_moving_cards;
   guint i;
   GByteArray *cards;
@@ -903,6 +940,7 @@ drag_begin (AisleriotBoard *board)
 #else
   const GdkColor masked = { 0, 0, 0, 0 };
   const GdkColor unmasked = { 1, 0xffff, 0xffff, 0xffff };
+  GdkBitmap *card_mask;
   GdkGC *gc1, *gc2;
   gboolean use_pixbuf_drawing = priv->use_pixbuf_drawing;
 #endif /* CAIRO_DRAWING */
@@ -986,17 +1024,12 @@ drag_begin (AisleriotBoard *board)
   cr2 = gdk_cairo_create (moving_mask);
   cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
   cairo_paint (cr2);
-  cairo_set_operator (cr2, CAIRO_OPERATOR_OVER);
 
-  cairo_set_source_rgba (cr2, 1., 1., 1., 0.);
+  cairo_set_operator (cr2, CAIRO_OPERATOR_SOURCE);
+  cairo_set_source_rgb (cr2, 0., 0., 0.);
 
-  card_mask = ar_card_images_get_card_mask (priv->images);
-
-//   gdk_cairo_set_source_pixmap (cr1, card_mask, 0, 0);
-//   gdk_cairo_set_source_pixmap (cr2, card_mask, 0, 0);
-
-  //FIXME?;
 #else
+
   gc1 = gdk_gc_new (moving_pixmap);
   gc2 = gdk_gc_new (moving_mask);
 
@@ -1025,26 +1058,24 @@ drag_begin (AisleriotBoard *board)
     Card hcard = CARD (priv->moving_cards->data[i]);
 
 #ifdef CAIRO_DRAWING
-    GdkPixbuf *pixbuf;
+    cairo_surface_t *surface;
+    cairo_pattern_t *pattern;
+    cairo_matrix_t matrix;
 
-    pixbuf = ar_card_images_get_card_pixbuf (priv->images, hcard, FALSE);
-    if (!pixbuf)
+    surface = ar_card_surface_cache_get_card_surface (priv->card_cache, hcard);
+    if (surface == NULL)
       goto next;
 
-    #if 0
-    gdk_draw_pixbuf (moving_pixmap, gc1,
-                      pixbuf,
-                      0, 0, x, y, width, height,
-                      GDK_RGB_DITHER_NONE, 0, 0);
-    gdk_draw_rectangle (moving_mask, gc2, TRUE,
-                        x, y, width, height);
-    #endif
+    pattern = cairo_pattern_create_for_surface (surface);
+    cairo_matrix_init_translate (&matrix, -x, -y);
+    cairo_pattern_set_matrix (pattern, &matrix);
+    cairo_set_source (cr1, pattern);
 
-    gdk_cairo_set_source_pixbuf (cr1, pixbuf, x, y);
     cairo_paint (cr1);
 
-    gdk_cairo_set_source_pixmap (cr2, card_mask, x, y);
-    cairo_paint (cr2);
+    cairo_mask (cr2, pattern);
+
+    cairo_pattern_destroy (pattern);
 
 #else /* !CAIRO_DRAWING */
     gdk_gc_set_clip_origin (gc1, x, y);
@@ -2382,22 +2413,19 @@ static void
 aisleriot_board_realize (GtkWidget *widget)
 {
   AisleriotBoard *board = AISLERIOT_BOARD (widget);
-  AisleriotBoardPrivate *priv = board->priv;
-  GdkDisplay *display;
-  GdkWindow *window;
 
   GTK_WIDGET_CLASS (aisleriot_board_parent_class)->realize (widget);
 
-  window = gtk_widget_get_window (widget);
-
-  display = gtk_widget_get_display (widget);
-
-  ar_card_images_set_drawable (priv->images, window);
-
 #ifndef CAIRO_DRAWING
 {
+  AisleriotBoardPrivate *priv = board->priv;
+  GdkWindow *window;
   GdkColor baize_color;
 
+  window = gtk_widget_get_window (widget);
+
+  ar_card_images_set_drawable (priv->images, window);
+
   priv->draw_gc = gdk_gc_new (window);
 
   priv->bg_gc = gdk_gc_new (window);
@@ -2409,12 +2437,21 @@ aisleriot_board_realize (GtkWidget *widget)
 }
 #endif /* !CAIRO_DRAWING */
 
-#ifndef HAVE_HILDON 
+#ifndef HAVE_HILDON
+{
+  AisleriotBoardPrivate *priv = board->priv;
+  GdkWindow *window;
+  GdkDisplay *display;
+
+  window = gtk_widget_get_window (widget);
+  display = gtk_widget_get_display (widget);
+
   /* Create cursors */
   priv->cursor[AR_CURSOR_DEFAULT] = gdk_cursor_new_for_display (display, GDK_LEFT_PTR);
   priv->cursor[AR_CURSOR_OPEN] = ar_cursor_new (window, AR_CURSOR_OPEN);
   priv->cursor[AR_CURSOR_CLOSED] = ar_cursor_new (window, AR_CURSOR_CLOSED);
   priv->cursor[AR_CURSOR_DROPPABLE] = gdk_cursor_new_for_display (display, GDK_DOUBLE_ARROW); /* FIXMEchpe: better cursor */
+}
 #endif /* !HAVE_HILDON */
 
   aisleriot_board_setup_geometry (board);
@@ -2431,13 +2468,18 @@ aisleriot_board_unrealize (GtkWidget *widget)
 
   priv->geometry_set = FALSE;
 
-#ifndef CAIRO_DRAWING
+#ifdef CAIRO_DRAWING
+  priv->slot_surface = NULL;
+#else
   g_object_unref (priv->draw_gc);
   priv->draw_gc = NULL;
   g_object_unref (priv->bg_gc);
   priv->bg_gc = NULL;
   g_object_unref (priv->slot_gc);
   priv->slot_gc = NULL;
+
+  ar_card_images_set_drawable (priv, NULL);
+  priv->slot_image = NULL;
 #endif
 
 #ifndef HAVE_HILDON 
@@ -2447,12 +2489,8 @@ aisleriot_board_unrealize (GtkWidget *widget)
   }
 #endif /* !HAVE_HILDON*/
 
-  ar_card_images_set_drawable (priv->images, NULL);
-
   clear_state (board);
 
-  priv->slot_image = NULL;
-
   GTK_WIDGET_CLASS (aisleriot_board_parent_class)->unrealize (widget);
 }
 
@@ -2485,9 +2523,14 @@ aisleriot_board_sync_style (ArStyle *style,
     theme = ar_style_get_card_theme (style);
     if (theme != NULL) {
       priv->geometry_set = FALSE;
-      priv->slot_image = NULL;
 
+#ifdef CAIRO_DRAWING
+      priv->slot_surface = NULL;
+      ar_card_surface_cache_set_theme (priv->card_cache, theme);
+#else
+      priv->slot_image = NULL;
       ar_card_images_set_theme (priv->images, theme);
+#endif
 
       update_geometry |= TRUE;
       queue_redraw |= TRUE;
@@ -2555,12 +2598,15 @@ aisleriot_board_sync_style (ArStyle *style,
   }
 
   if (pspec_name == NULL || pspec_name == I_(AR_STYLE_PROP_SELECTION_COLOR)) {
+#ifndef CAIRO_DRAWING
     GdkColor selection_color;
 
     ar_style_get_selection_color (priv->style, &selection_color);
     ar_card_images_set_selection_color (priv->images, &selection_color);
 
     /* FIXMEchpe: update the cached images in the selection slot!! */
+#endif
+
     redraw_selection = TRUE;
   }
 
@@ -3136,6 +3182,9 @@ aisleriot_board_expose_event (GtkWidget *widget,
   guint n_exposed_slots;
   GdkWindow *window;
   GdkColor color;
+  cairo_surface_t *surface;
+  cairo_pattern_t *pattern;
+  cairo_matrix_t matrix;
 
   window = gtk_widget_get_window (widget);
 
@@ -3167,7 +3216,10 @@ aisleriot_board_expose_event (GtkWidget *widget,
     g_print ("Exposing area %d:%d@(%d,%d) ", event->area.width, event->area.height,
              event->area.x, event->area.y);
     for (i = 0; i < n_rects; ++i) {
-      g_print ("[Rect %d:%d@(%d,%d)] ", rects[i].width, rects[i].height, rects[i].x, rects[i].y);
+      cairo_rectangle_int_t rect;
+
+      cairo_region_get_rectangle (region, i, &rect);
+      g_print ("[Rect %d:%d@(%d,%d)] ", rect.width, rect.height, rect.x, rect.y);
     }
     g_print ("\n");
   }
@@ -3186,11 +3238,6 @@ aisleriot_board_expose_event (GtkWidget *widget,
   for (i = 0; i < n_rects; ++i) {
     rect = &rects[i];
 
-#if 0
-    gdk_draw_rectangle (window, priv->bg_gc, TRUE,
-                        rect->x, rect->y,
-                        rect->width, rect->height);
-#endif
     cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
   }
   g_free (rects);
@@ -3234,10 +3281,15 @@ aisleriot_board_expose_event (GtkWidget *widget,
   /* First draw the slots. Otherwise they'll overlap on cards
    * (e.g. in Elevator, after a card was removed).
    */
+  if (priv->slot_surface == NULL)
+    goto draw_cards;
+
+  pattern = cairo_pattern_create_for_surface (priv->slot_surface);
+  cairo_set_source (cr, pattern);
+
   for (i = 0; i < n_exposed_slots; ++i) {
     ArSlot *hslot = exposed_slots[i];
     int x, y;
-    GdkPixbuf *pixbuf;
 
     /* FIXMEchpe: if ((hslot->length - hslot->exposed) >= 0) ?? */
     if (hslot->cards->len > 0)
@@ -3247,29 +3299,26 @@ aisleriot_board_expose_event (GtkWidget *widget,
     x = hslot->rect.x;
     y = hslot->rect.y;
 
-    #if 0
-    gdk_gc_set_clip_origin (priv->slot_gc, x, y);
-    #endif
+    cairo_matrix_init_translate (&matrix, -x, -y);
+    cairo_pattern_set_matrix (pattern, &matrix);
+    cairo_paint (cr);
 
-    if (G_LIKELY (hslot != highlight_slot)) {
-      pixbuf = priv->slot_image;
-    } else {
-      pixbuf = ar_card_images_get_slot_pixbuf (priv->images,
-                                               priv->show_highlight);
+    if (G_UNLIKELY (hslot == highlight_slot)) {
+      cairo_save (cr);
+      ar_style_get_selection_color (priv->style, &color);
+      cairo_set_source_rgba (cr,
+                             color.red / 65535.,
+                             color.green / 65535.,
+                             color.blue / 65535.,
+                             HIGHLIGHT_ALPHA);
+      cairo_mask (cr, pattern);
+      cairo_restore (cr);
     }
+  }
 
-    if (!pixbuf)
-      continue;
-
-    #if 0
-    gdk_draw_pixbuf (window, priv->slot_gc, pixbuf, 0, 0, x, y,
-                      priv->card_size.width, priv->card_size.height,
-                      GDK_RGB_DITHER_NONE, 0, 0);
-    #endif
+  cairo_pattern_destroy (pattern);
 
-    gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
-    cairo_paint (cr);
-  }
+draw_cards:
 
   /* Now draw the cards */
   for (i = 0; i < n_exposed_slots; ++i) {
@@ -3277,13 +3326,22 @@ aisleriot_board_expose_event (GtkWidget *widget,
     GByteArray *cards = hslot->cards;
     gpointer *card_images = hslot->card_images->pdata;
     GdkRectangle card_rect;
-    GdkPixbuf *pixbuf;
     guint j, n_cards;
+    int highlight_start_card_id = G_MAXINT;
 
     n_cards = cards->len;
     if (n_cards == 0)
       continue;
 
+    if (G_UNLIKELY (hslot == priv->highlight_slot &&
+                    priv->show_highlight)) {
+      highlight_start_card_id = hslot->cards->len - 1;
+    } else if (G_UNLIKELY (hslot == priv->selection_slot &&
+                           priv->selection_start_card_id >= 0 &&
+                           priv->show_selection)) {
+      highlight_start_card_id = priv->selection_start_card_id;
+    }
+
     card_rect.x = hslot->rect.x;
     card_rect.y = hslot->rect.y;
     card_rect.width = priv->card_size.width;
@@ -3308,20 +3366,30 @@ aisleriot_board_expose_event (GtkWidget *widget,
         goto next;
 #endif
 
-      pixbuf = card_images[j];
-      if (!pixbuf)
+      surface = card_images[j];
+      if (surface == NULL)
         goto next;
 
-      #if 0
-      gdk_gc_set_clip_origin (priv->draw_gc, card_rect.x, card_rect.y);
-      gdk_draw_pixbuf (window, priv->draw_gc, pixbuf,
-                        0, 0, card_rect.x, card_rect.y, -1, -1,
-                        GDK_RGB_DITHER_NONE, 0, 0);
-      #endif
+      pattern = cairo_pattern_create_for_surface (surface);
+      cairo_matrix_init_translate (&matrix, -card_rect.x, -card_rect.y);
+      cairo_pattern_set_matrix (pattern, &matrix);
+      cairo_set_source (cr, pattern);
+      cairo_pattern_destroy (pattern);
 
-      gdk_cairo_set_source_pixbuf (cr, pixbuf, card_rect.x, card_rect.y);
       cairo_paint (cr);
 
+      if (G_UNLIKELY (j >= highlight_start_card_id)) {
+        cairo_save (cr);
+        ar_style_get_selection_color (priv->style, &color);
+        cairo_set_source_rgba (cr,
+                               color.red / 65535.,
+                               color.green / 65535.,
+                               color.blue / 65535.,
+                               HIGHLIGHT_ALPHA);
+        cairo_mask (cr, pattern);
+        cairo_restore (cr);
+      }
+
     next:
 
       card_rect.x += hslot->pixeldx;
@@ -3339,25 +3407,23 @@ aisleriot_board_expose_event (GtkWidget *widget,
 #endif
   {
     GdkRectangle card_rect;
-    GdkPixbuf *pixbuf;
+    cairo_pattern_t *pattern;
 
     get_rect_by_slot_and_card (board,
                                priv->show_card_slot,
                                priv->show_card_id,
                                1, &card_rect);
 
-    pixbuf = priv->show_card_slot->card_images->pdata[priv->show_card_id];
-    if (!pixbuf)
+    surface = priv->show_card_slot->card_images->pdata[priv->show_card_id];
+    if (surface == NULL)
       goto draw_focus;
 
-    #if 0
-    gdk_gc_set_clip_origin (priv->draw_gc, card_rect.x, card_rect.y);
-    gdk_draw_pixbuf (window, priv->draw_gc, pixbuf,
-                      0, 0, card_rect.x, card_rect.y, -1, -1,
-                      GDK_RGB_DITHER_NONE, 0, 0);
-    #endif
+    pattern = cairo_pattern_create_for_surface (surface);
+    cairo_matrix_init_translate (&matrix, -card_rect.x, -card_rect.y);
+    cairo_pattern_set_matrix (pattern, &matrix);
+    cairo_set_source (cr, pattern);
+    cairo_pattern_destroy (pattern);
 
-    gdk_cairo_set_source_pixbuf (cr, pixbuf, card_rect.x, card_rect.y);
     cairo_paint (cr);
   }
 
@@ -3368,7 +3434,9 @@ draw_focus:
                   priv->focus_slot != NULL &&
                   gtk_widget_has_focus (widget))) {
     GdkRectangle focus_rect;
+    double line_width;
 
+  g_print ("focus!\n");
     /* Check whether this needs to be drawn */
 #if GTK_CHECK_VERSION (2, 90, 5)
     if (cairo_region_contains_rectangle (region, &priv->focus_rect) == CAIRO_REGION_OVERLAP_OUT)
@@ -3388,24 +3456,25 @@ draw_focus:
                                  &focus_rect);
     }
 
-    #if 0
-    gtk_paint_focus (gtk_widget_get_style (widget),
-                     window,
-                     gtk_widget_get_state (widget),
-                     &priv->focus_rect,
-                     widget,
-                     NULL /* FIXME ? */,
-                     focus_rect.x,
-                     focus_rect.y,
-                     focus_rect.width,
-                     focus_rect.height);
-    #endif
-    // FIXMEchpe
+    line_width = ar_style_get_focus_line_width (priv->style);
+    cairo_set_source_rgb (cr, 1., 1., 1.); /* FIXME focus line colour? */
+    cairo_set_line_width (cr, line_width);
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+
+    cairo_rectangle (cr,
+                     focus_rect.x + line_width / 2., focus_rect.y + line_width / 2.,
+                     focus_rect.width - line_width, focus_rect.height - line_width);
+    cairo_stroke (cr);
   }
 
 expose_done:
 #endif /* ENABLE_KEYNAV */
 
+  #if DEBUG_DRAWING
+  if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
+    g_print ("expose-event cairo status %d\n", cairo_status (cr));
+  }
+  #endif
   cairo_destroy (cr);
 
   /* Parent class has no expose handler, no need to chain up */
@@ -3752,9 +3821,11 @@ aisleriot_board_init (AisleriotBoard *board)
 
   priv->moving_cards = g_byte_array_sized_new (SLOT_CARDS_N_PREALLOC);
 
-  priv->images = ar_card_images_new ();
 #ifdef CAIRO_DRAWING
-  ar_card_images_set_cache_mode (priv->images, CACHE_PIXBUFS);
+  priv->card_cache = ar_card_surface_cache_new ();
+  // FIXMEchpe connect changed handler
+#else
+  priv->images = ar_card_images_new ();
 #endif
 
   gtk_widget_set_events (widget,
@@ -3812,7 +3883,11 @@ aisleriot_board_finalize (GObject *object)
 
   g_byte_array_free (priv->moving_cards, TRUE);
 
+#ifdef CAIRO_DRAWING
+  g_object_unref (priv->card_cache);
+#else
   g_object_unref (priv->images);
+#endif
 
   G_OBJECT_CLASS (aisleriot_board_parent_class)->finalize (object);
 }
diff --git a/aisleriot/lib/Makefile.am b/aisleriot/lib/Makefile.am
index 2df7958..833daba 100644
--- a/aisleriot/lib/Makefile.am
+++ b/aisleriot/lib/Makefile.am
@@ -5,8 +5,6 @@ noinst_LTLIBRARIES = libaisleriot.la
 libaisleriot_la_SOURCES = \
 	ar-card.c \
 	ar-card.h \
-	ar-card-images.c \
-	ar-card-images.h \
 	ar-card-private.h \
 	ar-card-theme.c \
 	ar-card-theme.h \
@@ -17,6 +15,19 @@ libaisleriot_la_SOURCES = \
 	ar-pixbuf-utils.h \
 	$(NULL)
 
+if HAVE_GTK_2
+libaisleriot_la_SOURCES += \
+	ar-card-images.c \
+	ar-card-images.h \
+	$(NULL)
+endif
+
+# FIXME conditional
+libaisleriot_la_SOURCES += \
+	ar-card-surface-cache.c \
+	ar-card-surface-cache.h \
+	$(NULL)
+
 if HAVE_CLUTTER
 libaisleriot_la_SOURCES += \
 	ar-card-textures-cache.c \
diff --git a/aisleriot/lib/ar-card-surface-cache.c b/aisleriot/lib/ar-card-surface-cache.c
new file mode 100644
index 0000000..db89059
--- /dev/null
+++ b/aisleriot/lib/ar-card-surface-cache.c
@@ -0,0 +1,349 @@
+/*
+  Copyright © 2008 Neil Roberts
+  Copyright © 2008, 2010 Christian Persch
+
+  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 3 of the License, 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.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <libgames-support/games-debug.h>
+
+#include "ar-card-surface-cache.h"
+#include "ar-card-private.h"
+
+struct _ArCardSurfaceCachePrivate
+{
+  ArCardTheme *theme;
+  guint theme_changed_id;
+
+  cairo_surface_t **cards;
+
+#ifdef GNOME_ENABLE_DEBUG
+  guint n_calls;
+  guint cache_hits;
+#endif
+};
+
+enum
+{
+  PROP_0,
+  PROP_THEME
+};
+
+/* This is an invalid value for a cairo_surface_t *, and distinct from COGL_INVALID_HANDLE */
+#define FAILED_SURFACE ((gpointer) 0x1)
+#define IS_FAILED_SURFACE(ptr) (G_UNLIKELY ((ptr) == FAILED_SURFACE))
+
+/* Logging */
+#ifdef GNOME_ENABLE_DEBUG
+#define LOG_CALL(obj) obj->priv->n_calls++
+#define LOG_CACHE_HIT(obj) obj->priv->cache_hits++
+#define LOG_CACHE_MISS(obj)
+#else
+#define LOG_CALL(obj)
+#define LOG_CACHE_HIT(obj)
+#define LOG_CACHE_MISS(obj)
+#endif /* GNOME_ENABLE_DEBUG */
+
+static void ar_card_surface_cache_dispose (GObject *object);
+static void ar_card_surface_cache_finalize (GObject *object);
+
+G_DEFINE_TYPE (ArCardSurfaceCache, ar_card_surface_cache, G_TYPE_OBJECT);
+
+#define AR_CARD_SURFACE_CACHE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), AR_TYPE_CARD_SURFACE_CACHE, ArCardSurfaceCachePrivate))
+
+/* Helper functions */
+
+static void
+ar_card_surface_cache_clear (ArCardSurfaceCache *cache)
+{
+  ArCardSurfaceCachePrivate *priv = cache->priv;
+  int i;
+
+  _games_debug_print (GAMES_DEBUG_CARD_CACHE,
+                      "ar_card_surface_cache_clear\n");
+
+  for (i = 0; i < AR_CARDS_TOTAL; i++) {
+    cairo_surface_t *surface = priv->cards[i];
+
+    if (surface != NULL &&
+        !IS_FAILED_SURFACE (surface)) {
+      cairo_surface_destroy (surface);
+    }
+
+    priv->cards[i] = NULL;
+  }
+}
+
+static void
+ar_card_surface_cache_unset_theme (ArCardSurfaceCache *cache)
+{
+  ArCardSurfaceCachePrivate *priv = cache->priv;
+
+  if (priv->theme) {
+    g_signal_handler_disconnect (priv->theme, priv->theme_changed_id);
+    g_object_unref (priv->theme);
+    priv->theme = NULL;
+    priv->theme_changed_id = 0;
+  }
+}
+
+/* Class implementation */
+
+static void
+ar_card_surface_cache_init (ArCardSurfaceCache *self)
+{
+  ArCardSurfaceCachePrivate *priv;
+
+  priv = self->priv = AR_CARD_SURFACE_CACHE_GET_PRIVATE (self);
+
+  priv->cards = g_malloc0 (sizeof (cairo_surface_t*) * AR_CARDS_TOTAL);
+}
+
+static void
+ar_card_surface_cache_dispose (GObject *object)
+{
+  ArCardSurfaceCache *cache = AR_CARD_SURFACE_CACHE (object);
+
+  ar_card_surface_cache_clear (cache);
+  ar_card_surface_cache_unset_theme (cache);
+
+  G_OBJECT_CLASS (ar_card_surface_cache_parent_class)->dispose (object);
+}
+
+static void
+ar_card_surface_cache_finalize (GObject *object)
+{
+  ArCardSurfaceCache *cache = AR_CARD_SURFACE_CACHE (object);
+  ArCardSurfaceCachePrivate *priv = cache->priv;
+
+  g_free (priv->cards);
+
+#ifdef GNOME_ENABLE_DEBUG
+  _GAMES_DEBUG_IF (GAMES_DEBUG_CARD_CACHE) {
+    _games_debug_print (GAMES_DEBUG_CARD_CACHE,
+                        "ArCardSurfaceCache %p statistics: %u calls with %u hits and %u misses for a hit/total of %.3f\n",
+                        cache, priv->n_calls, priv->cache_hits, priv->n_calls - priv->cache_hits,
+                        priv->n_calls > 0 ? (double) priv->cache_hits / (double) priv->n_calls : 0.0);
+  }
+#endif
+
+  G_OBJECT_CLASS (ar_card_surface_cache_parent_class)->finalize (object);
+}
+
+static void
+ar_card_surface_cache_set_property (GObject *self,
+                                        guint property_id,
+                                        const GValue *value,
+                                        GParamSpec *pspec)
+{
+  ArCardSurfaceCache *cache = AR_CARD_SURFACE_CACHE (self);
+
+  switch (property_id) {
+    case PROP_THEME:
+      ar_card_surface_cache_set_theme (cache, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+      break;
+    }
+}
+
+static void
+ar_card_surface_cache_get_property (GObject *self,
+                                        guint property_id,
+                                        GValue *value,
+                                        GParamSpec *pspec)
+{
+  ArCardSurfaceCache *cache = AR_CARD_SURFACE_CACHE (self);
+
+  switch (property_id) {
+    case PROP_THEME:
+      g_value_set_object (value, ar_card_surface_cache_get_theme (cache));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+      break;
+    }
+}
+
+static void
+ar_card_surface_cache_class_init (ArCardSurfaceCacheClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose = ar_card_surface_cache_dispose;
+  gobject_class->finalize = ar_card_surface_cache_finalize;
+  gobject_class->set_property = ar_card_surface_cache_set_property;
+  gobject_class->get_property = ar_card_surface_cache_get_property;
+
+  g_type_class_add_private (klass, sizeof (ArCardSurfaceCachePrivate));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_THEME, 
+                                   g_param_spec_object ("theme", NULL, NULL,
+                                                        AR_TYPE_CARD_THEME,
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+}
+
+/* Public API */
+
+/**
+ * ar_card_surface_cache_new:
+ *
+ * Returns: a new #ArCardSurfaceCache object
+ */
+ArCardSurfaceCache *
+ar_card_surface_cache_new (void)
+{
+  return g_object_new (AR_TYPE_CARD_SURFACE_CACHE, NULL);
+}
+
+/**
+ * ar_card_surface_cache_drop:
+ * @images: a #ArCardImages
+ *
+ * Clears the image cache.
+ */
+void
+ar_card_surface_cache_drop (ArCardSurfaceCache *cache)
+{
+  g_return_if_fail (AR_IS_CARD_SURFACE_CACHE (cache));
+
+  ar_card_surface_cache_clear (cache);
+}
+
+/**
+ * ar_card_surface_cache_set_theme:
+ * @cache:
+ * @theme:
+ *
+ * Sets the card theme.
+ */
+void
+ar_card_surface_cache_set_theme (ArCardSurfaceCache *cache,
+                                 ArCardTheme *theme)
+{
+  ArCardSurfaceCachePrivate *priv = cache->priv;
+
+  g_return_if_fail (AR_IS_CARD_SURFACE_CACHE (cache));
+  g_return_if_fail (theme == NULL || AR_IS_CARD_THEME (theme));
+
+  if (priv->theme == theme)
+    return;
+
+  ar_card_surface_cache_clear (cache);
+  ar_card_surface_cache_unset_theme (cache);
+
+  priv->theme = theme;
+  if (theme) {
+    g_object_ref (theme);
+
+    priv->theme_changed_id = g_signal_connect_swapped (theme, "changed",
+                                                       G_CALLBACK (ar_card_surface_cache_clear),
+                                                       cache);
+  }
+
+  g_object_notify (G_OBJECT (cache), "theme");
+}
+
+/**
+ * ar_card_surface_cache_get_theme:
+ * @cache:
+ *
+ * Returns: the the card theme of @cache
+ */
+ArCardTheme *
+ar_card_surface_cache_get_theme (ArCardSurfaceCache *cache)
+{
+  g_return_val_if_fail (AR_IS_CARD_SURFACE_CACHE (cache), NULL);
+
+  return cache->priv->theme;
+}
+
+/**
+ * ar_card_surface_cache_get_card_surface_by_id:
+ * @cache:
+ * @card_id:
+ *
+ * Returns: a cached #cairo_surface_t for @card_id.
+ */
+cairo_surface_t *
+ar_card_surface_cache_get_card_surface_by_id (ArCardSurfaceCache *cache,
+                                              guint card_id)
+{
+  ArCardSurfaceCachePrivate *priv = cache->priv;
+  cairo_surface_t *surface;
+
+  g_return_val_if_fail (card_id < AR_CARDS_TOTAL , NULL);
+
+  LOG_CALL (cache);
+
+  surface = priv->cards[card_id];
+  if (IS_FAILED_SURFACE (surface)) {
+    LOG_CACHE_HIT (cache);
+    return NULL;
+  }
+
+  if (surface == NULL) {
+    LOG_CACHE_MISS (cache);
+
+    surface = ar_card_theme_get_card_surface (priv->theme, card_id);
+    if (surface == NULL) {
+      priv->cards[card_id] = FAILED_SURFACE;
+      return NULL;
+    }
+
+    priv->cards[card_id] = surface; /* adopts */
+  } else {
+    LOG_CACHE_HIT (cache);
+  }
+
+  return surface;
+}
+
+/**
+ * ar_card_surface_cache_get_card_surface:
+ * @cache:
+ * @card:
+ * @highlighted:
+ *
+ * Returns: a cached #cairo_surface_t for @card.
+ */
+cairo_surface_t *
+ar_card_surface_cache_get_card_surface (ArCardSurfaceCache *cache,
+                                            Card card)
+{
+  guint card_id = _ar_card_to_index (card);
+
+  return ar_card_surface_cache_get_card_surface_by_id (cache, card_id);
+}
+
+/**
+ * ar_card_surface_cache_get_slot_surface:
+ * @cache:
+ * @highlighted:
+ *
+ * Returns: a cached #cairo_surface_t * for the slot.
+ */
+cairo_surface_t *
+ar_card_surface_cache_get_slot_surface (ArCardSurfaceCache *cache)
+{
+  return ar_card_surface_cache_get_card_surface_by_id (cache, AR_CARD_SLOT);
+}
diff --git a/aisleriot/lib/ar-card-surface-cache.h b/aisleriot/lib/ar-card-surface-cache.h
new file mode 100644
index 0000000..3e14f6d
--- /dev/null
+++ b/aisleriot/lib/ar-card-surface-cache.h
@@ -0,0 +1,74 @@
+/*
+  Copyright © 2008 Neil Roberts
+  Copyright © 2008, 2010 Christian Persch
+
+  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 3 of the License, 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.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef AR_CARD_SURFACE_CACHE_H
+#define AR_CARD_SURFACE_CACHE_H
+
+#include <glib-object.h>
+#include <cairo.h>
+
+#include "ar-card.h"
+#include "ar-card-theme.h"
+
+G_BEGIN_DECLS
+
+#define AR_TYPE_CARD_SURFACE_CACHE            (ar_card_surface_cache_get_type())
+#define AR_CARD_SURFACE_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), AR_TYPE_CARD_SURFACE_CACHE, ArCardSurfaceCache))
+#define AR_CARD_SURFACE_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), AR_TYPE_CARD_SURFACE_CACHE, ArCardSurfaceCacheClass))
+#define AR_IS_CARD_SURFACE_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AR_TYPE_CARD_SURFACE_CACHE))
+#define AR_IS_CARD_SURFACE_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), AR_TYPE_CARD_SURFACE_CACHE))
+#define AR_CARD_SURFACE_CACHE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), AR_TYPE_CARD_SURFACE_CACHE, ArCardSurfaceCacheClass))
+
+typedef struct _ArCardSurfaceCache        ArCardSurfaceCache;
+typedef struct _ArCardSurfaceCacheClass   ArCardSurfaceCacheClass;
+typedef struct _ArCardSurfaceCachePrivate ArCardSurfaceCachePrivate;
+
+struct _ArCardSurfaceCacheClass
+{
+  GObjectClass parent_class;
+};
+
+struct _ArCardSurfaceCache
+{
+  GObject parent;
+
+  ArCardSurfaceCachePrivate *priv;
+};
+
+GType ar_card_surface_cache_get_type (void);
+
+ArCardSurfaceCache *ar_card_surface_cache_new (void);
+
+void ar_card_surface_cache_drop (ArCardSurfaceCache *cache);
+
+void ar_card_surface_cache_set_theme (ArCardSurfaceCache *cache,
+                                      ArCardTheme *theme);
+
+ArCardTheme *ar_card_surface_cache_get_theme (ArCardSurfaceCache *cache);
+
+cairo_surface_t *ar_card_surface_cache_get_card_surface (ArCardSurfaceCache *cache,
+                                                         Card card);
+
+cairo_surface_t *ar_card_surface_cache_get_card_surface_by_id (ArCardSurfaceCache *cache,
+                                                               guint card_id);
+
+cairo_surface_t *ar_card_surface_cache_get_slot_surface (ArCardSurfaceCache *cache);
+
+G_END_DECLS
+
+#endif /* AR_CARD_SURFACE_CACHE_H */
diff --git a/aisleriot/lib/ar-card-theme-private.h b/aisleriot/lib/ar-card-theme-private.h
index 5943520..71aba3c 100644
--- a/aisleriot/lib/ar-card-theme-private.h
+++ b/aisleriot/lib/ar-card-theme-private.h
@@ -83,6 +83,8 @@ struct _ArCardThemeClass {
                                      int card_id);
 
 #if GTK_CHECK_VERSION (2, 10, 0)
+  cairo_surface_t* (* get_card_surface) (ArCardTheme *theme,
+                                         int card_id);
   void        (* set_font_options)  (ArCardTheme *theme,
                                      const cairo_font_options_t *font_options);
 #endif
diff --git a/aisleriot/lib/ar-card-theme.c b/aisleriot/lib/ar-card-theme.c
index fdba51a..443c1ba 100644
--- a/aisleriot/lib/ar-card-theme.c
+++ b/aisleriot/lib/ar-card-theme.c
@@ -112,6 +112,33 @@ ar_card_theme_class_get_theme_info (ArCardThemeClass *klass,
   return NULL;
 }
 
+#if GTK_CHECK_VERSION (2, 10,0)
+static cairo_surface_t *
+ar_card_theme_class_real_get_card_surface (ArCardTheme *theme,
+                                           int cardid)
+{
+  GdkPixbuf *pixbuf;
+  cairo_surface_t *surface;
+  cairo_t *cr;
+
+  pixbuf = ar_card_theme_get_card_pixbuf (theme, cardid);
+  if (pixbuf == NULL)
+    return NULL;
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        gdk_pixbuf_get_width (pixbuf),
+                                        gdk_pixbuf_get_height (pixbuf));
+  cr = cairo_create (surface);
+  gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+  g_object_unref (pixbuf);
+
+  return surface;
+}
+#endif /* GTK 2.10 */
+
 static void
 ar_card_theme_class_init (ArCardThemeClass * klass)
 {
@@ -123,6 +150,10 @@ ar_card_theme_class_init (ArCardThemeClass * klass)
 
   klass->get_theme_info = ar_card_theme_class_get_theme_info;
 
+#if GTK_CHECK_VERSION (2, 10,0)
+  klass->get_card_surface = ar_card_theme_class_real_get_card_surface;
+#endif
+
   g_object_class_install_property
     (gobject_class,
      PROP_THEME_INFO,
@@ -333,6 +364,35 @@ ar_card_theme_get_card_pixbuf (ArCardTheme *theme,
   return pixbuf;
 }
 
+#if GTK_CHECK_VERSION (2, 10,0)
+/**
+* ar_card_theme_get_card_surface:
+* @theme:
+* @card_id:
+*
+* Returns a #cairo_surface_t for the selected card using the currently loaded
+* theme and the currently selected size.
+*
+* Returns: a new #cairo_surface_t, or %NULL if there was an error
+*/
+cairo_surface_t *
+ar_card_theme_get_card_surface (ArCardTheme *theme,
+                                int cardid)
+{
+  cairo_surface_t *surface = NULL;
+
+  g_return_val_if_fail ((cardid >= 0) && (cardid < AR_CARDS_TOTAL), NULL);
+
+  _games_profile_start ("loading card %d from theme %s", cardid, theme->theme_info->display_name);
+
+  surface = theme->klass->get_card_surface (theme, cardid);
+
+  _games_profile_end ("loading card %d from theme %s", cardid, theme->theme_info->display_name);
+
+  return surface;
+}
+#endif /* GTK 2.10 */
+
 /* ArCardThemeInfo impl */
 
 static int
diff --git a/aisleriot/lib/ar-card-theme.h b/aisleriot/lib/ar-card-theme.h
index 53dba54..dce2b18 100644
--- a/aisleriot/lib/ar-card-theme.h
+++ b/aisleriot/lib/ar-card-theme.h
@@ -93,7 +93,12 @@ void ar_card_theme_get_size (ArCardTheme *theme,
 double ar_card_theme_get_aspect (ArCardTheme * theme);
 
 GdkPixbuf *ar_card_theme_get_card_pixbuf (ArCardTheme * theme,
-                                             int cardid);
+                                          int cardid);
+
+#if GTK_CHECK_VERSION (2, 10,0)
+cairo_surface_t *ar_card_theme_get_card_surface (ArCardTheme *theme,
+                                                 int cardid);
+#endif
 
 G_END_DECLS
 
diff --git a/configure.in b/configure.in
index fd60098..3c0af82 100644
--- a/configure.in
+++ b/configure.in
@@ -332,6 +332,9 @@ esac
 AC_SUBST([GTK_API_VERSION])
 AC_SUBST([GTK_API_MAJOR_VERSION])
 
+AM_CONDITIONAL([HAVE_GTK_2],[test "$GTK_API_VERSION" = "2.0"])
+AM_CONDITIONAL([HAVE_GTK_3],[test "$GTK_API_VERSION" = "3.0"])
+
 # Win32 platform
 
 AC_CANONICAL_HOST
diff --git a/libgames-support/games-preimage.c b/libgames-support/games-preimage.c
index 35d7606..1cf4e1b 100644
--- a/libgames-support/games-preimage.c
+++ b/libgames-support/games-preimage.c
@@ -158,6 +158,67 @@ cairo_pixels_to_pixbuf (guint8 * pixels, int rowstride, int height)
 }
 
 /**
+ * games_preimage_render_cairo_sub:
+ * @preimage:
+ * @cr:
+ * @node: a SVG node ID (starting with "#"), or %NULL
+ * @width: the width of the clip region
+ * @height: the height of the clip region
+ * @xoffset: the x offset of the clip region
+ * @yoffset: the y offset of the clip region
+ * @xzoom: the x zoom factor
+ * @yzoom: the y zoom factor
+ *
+ * Creates a #GdkPixbuf with the dimensions @width by @height,
+ * and renders the subimage of @preimage specified by @node to it,
+ * transformed by @xzoom, @yzoom and offset by @xoffset and @yoffset,
+ * clipped to @width and @height.
+ * If @node is NULL, the whole image is rendered into tha clip region.
+ *
+ * Returns: %TRUE, of %FALSE if there was an error or @preimage
+ * isn't a scalable SVG image
+ */
+gboolean
+games_preimage_render_cairo_sub (GamesPreimage * preimage,
+                                 cairo_surface_t *surface,
+                                 const char *node,
+                                 int width,
+                                 int height,
+                                 double xoffset,
+                                 double yoffset,
+                                 double xzoom,
+                                 double yzoom)
+{
+  cairo_t *cx;
+  cairo_matrix_t matrix;
+  gboolean success;
+
+  if (!preimage->scalable)
+    return FALSE;
+
+  cx = cairo_create (surface);
+
+  if (preimage->font_options) {
+    cairo_set_antialias (cx, cairo_font_options_get_antialias (preimage->font_options));
+
+    cairo_set_font_options (cx, preimage->font_options);
+  }
+
+  cairo_matrix_init_identity (&matrix);
+  cairo_matrix_scale (&matrix, xzoom, yzoom);
+  cairo_matrix_translate (&matrix, xoffset, yoffset);
+
+  cairo_set_matrix (cx, &matrix);
+
+  rsvg_handle_render_cairo_sub (preimage->rsvg_handle, cx, node);
+
+  success = (cairo_status (cx) == CAIRO_STATUS_SUCCESS);
+  cairo_destroy (cx);
+
+  return success;
+}
+
+/**
  * games_preimage_render_sub:
  * @preimage:
  * @node: a SVG node ID (starting with "#"), or %NULL
@@ -185,12 +246,9 @@ games_preimage_render_sub (GamesPreimage * preimage,
                            double xoffset,
                            double yoffset, double xzoom, double yzoom)
 {
-  GdkPixbuf *pixbuf = NULL;
   int rowstride;
   guint8 *data;
   cairo_surface_t *surface;
-  cairo_t *cx;
-  cairo_matrix_t matrix;
 
   if (!preimage->scalable)
     return NULL;
@@ -208,47 +266,24 @@ games_preimage_render_sub (GamesPreimage * preimage,
   surface = cairo_image_surface_create_for_data (data,
                                                  CAIRO_FORMAT_ARGB32,
                                                  width, height, rowstride);
-  if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+  if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS ||
+      !games_preimage_render_cairo_sub (preimage, surface, node, width, height,
+                                        xoffset, yoffset, xzoom, yzoom)) {
+    cairo_surface_destroy (surface);
     g_free (data);
     return NULL;
   }
 
-  cx = cairo_create (surface);
-
-  if (preimage->font_options) {
-    cairo_set_antialias (cx, cairo_font_options_get_antialias (preimage->font_options));
-
-    cairo_set_font_options (cx, preimage->font_options);
-  }
-
-  cairo_matrix_init_identity (&matrix);
-  cairo_matrix_scale (&matrix, xzoom, yzoom);
-  cairo_matrix_translate (&matrix, xoffset, yoffset);
-
-  cairo_set_matrix (cx, &matrix);
-
-  rsvg_handle_render_cairo_sub (preimage->rsvg_handle, cx, node);
-
-  cairo_pixels_to_pixbuf (data, rowstride, height);
-
-  if (cairo_status (cx) == CAIRO_STATUS_SUCCESS) {
-    pixbuf = gdk_pixbuf_new_from_data (data,
-                                       GDK_COLORSPACE_RGB,
-                                       TRUE,
-                                       8,
-                                       width, height,
-                                       rowstride,
-                                       (GdkPixbufDestroyNotify) g_free, NULL);
-    data = NULL;
-  }
-
-  cairo_destroy (cx);
-
   cairo_surface_destroy (surface);
+  cairo_pixels_to_pixbuf (data, rowstride, height);
 
-  g_free (data);
-
-  return pixbuf;
+  return gdk_pixbuf_new_from_data (data,
+                                   GDK_COLORSPACE_RGB,
+                                   TRUE,
+                                   8,
+                                   width, height,
+                                   rowstride,
+                                   (GdkPixbufDestroyNotify) g_free, NULL);
 }
 
 #endif /* HAVE_RSVG */
diff --git a/libgames-support/games-preimage.h b/libgames-support/games-preimage.h
index f44ea1a..8c821dc 100644
--- a/libgames-support/games-preimage.h
+++ b/libgames-support/games-preimage.h
@@ -59,6 +59,16 @@ GdkPixbuf *games_preimage_render_sub (GamesPreimage * preimage,
                                       double yoffset,
                                       double xzoom, double yzoom);
 
+gboolean games_preimage_render_cairo_sub (GamesPreimage * preimage,
+                                          cairo_surface_t *surface,
+                                          const char *node,
+                                          int width,
+                                          int height,
+                                          double xoffset,
+                                          double yoffset,
+                                          double xzoom,
+                                          double yzoom);
+
 gboolean games_preimage_is_scalable (GamesPreimage * preimage);
 
 gint games_preimage_get_width (GamesPreimage * preimage);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]