[gnome-games] gnometris: First pass as implementing a GObject-based texture cache; has issues



commit 459603c43a158b98f1912e0a8e508fcd5703e979
Author: Jason D. Clinton <me jasonclinton com>
Date:   Tue Aug 25 22:36:50 2009 -0500

    gnometris: First pass as implementing a GObject-based texture cache; has issues

 gnometris/Makefile.am          |    4 +-
 gnometris/blockops.cpp         |  127 ++++------------
 gnometris/blockops.h           |   39 +----
 gnometris/blocks-cache.cpp     |  327 ++++++++++++++++++++++++++++++++++++++++
 gnometris/blocks-cache.h       |   65 ++++++++
 gnometris/blocks.cpp           |   72 +++++++++
 gnometris/blocks.h             |   35 +++++
 gnometris/preview.cpp          |   37 ++----
 gnometris/preview.h            |    8 +-
 gnometris/renderer.cpp         |   83 ++---------
 gnometris/renderer.h           |   25 +---
 gnometris/tetris.cpp           |    4 +-
 gnometris/tetris.h             |    2 +-
 libgames-support/games-debug.c |    1 +
 libgames-support/games-debug.h |    7 +-
 15 files changed, 574 insertions(+), 262 deletions(-)
---
diff --git a/gnometris/Makefile.am b/gnometris/Makefile.am
index d2154c2..7823885 100644
--- a/gnometris/Makefile.am
+++ b/gnometris/Makefile.am
@@ -21,7 +21,9 @@ gnometris_SOURCES = \
 	blockops.cpp \
 	blockops.h \
 	renderer.cpp \
-	renderer.h
+	renderer.h \
+	blocks-cache.cpp \
+	blocks-cache.h
 
 gnometris_CPPFLAGS = \
 	-I$(top_srcdir) \
diff --git a/gnometris/blockops.cpp b/gnometris/blockops.cpp
index 385bfdc..a5d24b1 100644
--- a/gnometris/blockops.cpp
+++ b/gnometris/blockops.cpp
@@ -22,89 +22,14 @@
  */
 
 #include <config.h>
+
 #include "blockops.h"
-#include "blocks.h"
+#include <cairo/cairo.h>
 #include <clutter-gtk/clutter-gtk.h>
+#include "tetris.h"
 
 #define FONT "Sans Bold"
 
-Block::Block ():
-	what(EMPTY),
-	actor(NULL),
-	x(0),
-	y(0),
-	move_behaviour(NULL),
-	fall_behaviour(NULL),
-	explode_move_behaviour(NULL)
-{}
-
-Block::~Block ()
-{
-	if (actor)
-		clutter_actor_destroy (CLUTTER_ACTOR(actor));
-	if (move_behaviour)
-		g_object_unref (move_behaviour);
-	if (fall_behaviour)
-		g_object_unref (fall_behaviour);
-	if (explode_move_behaviour)
-		g_object_unref (explode_move_behaviour);
-}
-
-void
-Block::createActor (ClutterActor *chamber, cairo_surface_t *texture_source)
-{
-	if (actor)
-		clutter_actor_destroy (CLUTTER_ACTOR(actor));
-	actor = clutter_texture_new ();
-	// FIXME jclinton ... this should really be cluttercairo somehow for efficiency
-	clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE(actor),
-					   cairo_image_surface_get_data(texture_source),
-					   TRUE,
-					   cairo_image_surface_get_width(texture_source),
-					   cairo_image_surface_get_height(texture_source),
-					   cairo_image_surface_get_stride(texture_source),
-					   32, CLUTTER_TEXTURE_NONE, NULL);
-	clutter_group_add (CLUTTER_GROUP (chamber), actor);
-	clutter_actor_set_position (CLUTTER_ACTOR(actor), x, y);
-	clutter_actor_show (CLUTTER_ACTOR(actor));
-}
-
-void
-Block::bindAnimations (BlockOps *f)
-{
-	move_behaviour = clutter_behaviour_path_new_with_knots (f->move_alpha,
-								NULL, 0);
-
-	fall_behaviour = clutter_behaviour_path_new_with_knots (f->fall_alpha,
-								NULL, 0);
-
-	explode_move_behaviour = clutter_behaviour_path_new_with_knots (f->explode_alpha,
-									NULL, 0);
-}
-
-Block&
-Block::moveFrom (Block& b, BlockOps *f)
-{
-	if (this != &b) {
-		what = b.what;
-		b.what = EMPTY;
-		color = b.color;
-		b.color = 0;
-		if (b.actor) {
-			const ClutterKnot knot_line[] = {{b.x, b.y}, {x, y}};
-			fall_behaviour = clutter_behaviour_path_new_with_knots (f->fall_alpha,
-										knot_line, 2);
-			clutter_behaviour_apply (fall_behaviour, b.actor);
-			f->fall_behaviours = g_list_prepend (f->fall_behaviours, fall_behaviour);
-		}
-		actor = b.actor;
-		b.actor = NULL;
-	}
-	return *this;
-}
-
-/******************************************************************************/
-
 gboolean
 BlockOps::move_end (ClutterTimeline *time, BlockOps *f)
 {
@@ -156,7 +81,7 @@ BlockOps::BlockOps() :
 	height(0),
 	cell_width(0),
 	cell_height(0),
-	renderer(NULL),
+	cache(NULL),
 	themeID(-1),
 	blocknr(0),
 	rot(0),
@@ -178,7 +103,7 @@ BlockOps::BlockOps() :
 	stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (w));
 
 	playingField = clutter_group_new ();
-	clutter_group_add (CLUTTER_GROUP(stage), CLUTTER_ACTOR(playingField));
+	clutter_stage_add (stage, playingField);
 	
 	move_time = clutter_timeline_new (60);
 	g_signal_connect (move_time, "completed", G_CALLBACK
@@ -635,9 +560,13 @@ BlockOps::emptyField(int filled_lines, int fill_prob)
 				guint tmpColor = g_random_int_range(0, NCOLOURS);
 				field[x][y].what = LAYING;
 				field[x][y].color = tmpColor;
-				field[x][y].createActor (playingField, renderer->getCacheCellById (tmpColor));
+				field[x][y].createActor (playingField,
+				                         blocks_cache_get_block_texture_by_id (cache,
+				                                                               tmpColor),
+				                         cell_width,
+				                         cell_height);
 				clutter_actor_set_position (CLUTTER_ACTOR(field[x][y].actor),
-							    x*(cell_width), y*(cell_height));
+				                            x*(cell_width), y*(cell_height));
 			}
 		}
 	}
@@ -662,7 +591,10 @@ BlockOps::putBlockInField (SlotType fill)
 				field[i][j].color = color;
 				if (fill == FALLING) {
 					field[i][j].createActor (playingField,
-								 renderer->getCacheCellById (color));
+					                         blocks_cache_get_block_texture_by_id (cache,
+					                                                               color),
+								 cell_width,
+								 cell_height);
 				} else {
 					if (field[i][j].actor) {
 						clutter_actor_destroy (field[i][j].actor);
@@ -735,6 +667,7 @@ BlockOps::resize(GtkWidget *widget, GtkAllocation *allocation, BlockOps *field)
 {
 	field->width = allocation->width;
 	field->height = allocation->height;
+
 	if (field->width == 0 || field->height == 0)
 		return FALSE;
 	field->cell_width = field->width/COLUMNS;
@@ -769,11 +702,12 @@ BlockOps::rescaleField ()
 
 	cairo_t *bg_cr;
 
-	if (renderer)
-		renderer->rescaleCache (cell_width, cell_height);
+	/*if (cache)
+		g_object_unref (cache);
 	else {
-		renderer = rendererFactory (themeID, cell_width, cell_height);
-	}
+		cache = blocks_cache_new ();
+		blocks_cache_set_theme (cache, themeID);
+	}*/
 
 	if (background) {
 		clutter_actor_set_size (CLUTTER_ACTOR(background), width, height);
@@ -786,8 +720,9 @@ BlockOps::rescaleField ()
 		ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff };
 		clutter_stage_set_color (CLUTTER_STAGE (stage),
 					 &stage_color);
-		clutter_group_add (CLUTTER_GROUP (stage),
+		clutter_stage_add (stage,
 				   background);
+		clutter_actor_set_position (CLUTTER_ACTOR (background), 0, 0);
 	}
 
 	rescaleBlockPos ();
@@ -799,8 +734,9 @@ BlockOps::rescaleField ()
 							width, height);
 	} else {
 		foreground = clutter_cairo_texture_new (width, height);
-		clutter_group_add (CLUTTER_GROUP (stage),
+		clutter_stage_add (stage,
 				   foreground);
+		clutter_actor_set_position (CLUTTER_ACTOR (foreground), 0, 0);
 	}
 
 	bg_cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE(background));
@@ -837,8 +773,6 @@ BlockOps::rescaleField ()
 			center_anchor_x, center_anchor_y);
 	clutter_actor_raise (CLUTTER_ACTOR (playingField),
 			CLUTTER_ACTOR(background));
-
-	clutter_actor_show_all (stage);
 }
 
 void
@@ -852,7 +786,6 @@ BlockOps::drawMessage()
 	char *msg;
 
 	cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE(foreground));
-	clutter_actor_raise_top (CLUTTER_ACTOR(foreground));
 	cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
 	cairo_paint(cr);
 	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
@@ -957,13 +890,11 @@ BlockOps::setTheme (gint id)
 		return;
 
 	themeID = id;
-	if (renderer) {
-		delete renderer;
-		renderer = rendererFactory (themeID, cell_width,
-					    cell_height);
+	if (cache) {
+		blocks_cache_set_theme (cache, themeID);
 	} else {
-		renderer = rendererFactory (themeID, cell_width,
-					    cell_height);
+		cache = blocks_cache_new ();
+		blocks_cache_set_theme (cache, themeID);
 	}
 	rescaleBlockPos();
 }
diff --git a/gnometris/blockops.h b/gnometris/blockops.h
index 54ea06c..3ce265c 100644
--- a/gnometris/blockops.h
+++ b/gnometris/blockops.h
@@ -22,39 +22,12 @@
  * For more details see the file COPYING.
  */
 
-#include "tetris.h"
-#include "renderer.h"
-#include <clutter/clutter.h>
-
-enum SlotType {
-	EMPTY,
-	FALLING,
-	LAYING
-};
-
-class Block {
-public:
-	Block ();
-	~Block ();
-
-	Block& moveFrom (Block &b, BlockOps *f);
+#include "blocks.h"
+#include "blocks-cache.h"
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
 
-	SlotType what;
-	guint color;
-	ClutterActor *actor;
-
-	int x;
-	int y;
-
-	void createActor (ClutterActor *chamber, cairo_surface_t *texture_source);
-	void bindAnimations (BlockOps *f);
-
-	/* Every block will have a unique position
-	 * These can be continuously cleared and repopulated with new paths */
-	ClutterBehaviour *move_behaviour;
-	ClutterBehaviour *fall_behaviour;
-	ClutterBehaviour *explode_move_behaviour;
-};
+//typedef struct _BlocksCache BlocksCache;
 
 class BlockOps {
 	friend class Block;
@@ -131,7 +104,7 @@ private:
 	guint height;
 	guint cell_width;
 	guint cell_height;
-	Renderer *renderer;
+	BlocksCache *cache;
 	gint themeID;
 
 	Block **field;
diff --git a/gnometris/blocks-cache.cpp b/gnometris/blocks-cache.cpp
new file mode 100644
index 0000000..98c0144
--- /dev/null
+++ b/gnometris/blocks-cache.cpp
@@ -0,0 +1,327 @@
+/*
+  Copyright © 2008 Neil Roberts
+  Copyright © 2008 Christian Persch
+  Copyright © 2009 Jason D. Clinton
+
+  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 <glib-object.h>
+#include <cairo.h>
+#include <cogl/cogl.h>
+
+#include "blocks.h"
+#include "renderer.h"
+#include "blocks-cache.h"
+
+#include "../libgames-support/games-debug.h"
+
+struct _BlocksCachePrivate
+{
+  guint theme;
+  //guint theme_changed_handler;
+
+  CoglHandle *colours;
+
+#ifdef GNOME_ENABLE_DEBUG
+  guint n_calls;
+  guint cache_hits;
+#endif
+};
+
+enum
+{
+  PROP_0,
+  PROP_THEME
+};
+
+/* This is an invalid value for a CoglHandle, and distinct from COGL_INVALID_HANDLE */
+#define FAILED_HANDLE ((gpointer) 0x1)
+#define IS_FAILED_HANDLE(ptr) (G_UNLIKELY ((ptr) == FAILED_HANDLE))
+
+/* 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 blocks_cache_dispose (GObject *object);
+static void blocks_cache_finalize (GObject *object);
+
+G_DEFINE_TYPE (BlocksCache, blocks_cache, G_TYPE_OBJECT);
+
+#define BLOCKS_CACHE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TYPE_BLOCKS_CACHE, BlocksCachePrivate))
+
+/* Helper functions */
+
+static void
+blocks_cache_clear (BlocksCache *cache)
+{
+  BlocksCachePrivate *priv = cache->priv;
+  int i;
+
+  _games_debug_print (GAMES_DEBUG_BLOCKS_CACHE,
+                      "blocks_cache_clear\n");
+
+  for (i = 0; i < NCOLOURS; i++) {
+    CoglHandle handle = priv->colours[i];
+
+    if (handle != COGL_INVALID_HANDLE &&
+        !IS_FAILED_HANDLE (handle)) {
+      cogl_texture_unref (handle);
+    }
+
+    priv->colours[i] = COGL_INVALID_HANDLE;
+  }
+}
+
+static void
+blocks_cache_unset_theme (BlocksCache *cache)
+{
+  BlocksCachePrivate *priv = cache->priv;
+
+  //g_signal_handler_disconnect (GUINT_TO_POINTER(priv->theme), priv->theme_changed_handler);
+  priv->theme = NULL;
+  //priv->theme_changed_handler = 0;
+}
+
+/* Class implementation */
+
+static void
+blocks_cache_init (BlocksCache *self)
+{
+  BlocksCachePrivate *priv;
+
+  priv = self->priv = BLOCKS_CACHE_GET_PRIVATE (self);
+
+  priv->colours = static_cast<void**>(g_malloc0 (sizeof (CoglHandle) * NCOLOURS));
+}
+
+static void
+blocks_cache_dispose (GObject *object)
+{
+  BlocksCache *cache = BLOCKS_CACHE (object);
+
+  blocks_cache_clear (cache);
+  blocks_cache_unset_theme (cache);
+
+  G_OBJECT_CLASS (blocks_cache_parent_class)->dispose (object);
+}
+
+static void
+blocks_cache_finalize (GObject *object)
+{
+  BlocksCache *cache = BLOCKS_CACHE (object);
+  BlocksCachePrivate *priv = cache->priv;
+
+  g_free (priv->colours);
+
+#ifdef GNOME_ENABLE_DEBUG
+  _GAMES_DEBUG_IF (GAMES_DEBUG_BLOCKS_CACHE) {
+    _games_debug_print (GAMES_DEBUG_BLOCKS_CACHE,
+                        "BlocksCache %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 (blocks_cache_parent_class)->finalize (object);
+}
+
+static void
+blocks_cache_set_property (GObject *self,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+  BlocksCache *cache = BLOCKS_CACHE (self);
+
+  switch (property_id) {
+    case PROP_THEME:
+      blocks_cache_set_theme (cache, g_value_get_uint (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+      break;
+    }
+}
+
+static void
+blocks_cache_get_property (GObject *self,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+  BlocksCache *cache = BLOCKS_CACHE (self);
+
+  switch (property_id) {
+    case PROP_THEME:
+      g_value_set_object (value, GUINT_TO_POINTER(blocks_cache_get_theme (cache)));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
+      break;
+    }
+}
+
+static void
+blocks_cache_class_init (BlocksCacheClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  gobject_class->dispose = blocks_cache_dispose;
+  gobject_class->finalize = blocks_cache_finalize;
+  gobject_class->set_property = blocks_cache_set_property;
+  gobject_class->get_property = blocks_cache_get_property;
+
+  g_type_class_add_private (klass, sizeof (BlocksCachePrivate));
+
+  pspec = g_param_spec_uint ("theme", NULL, NULL,
+                               0, 2, 0,
+                               static_cast<GParamFlags>(G_PARAM_WRITABLE |
+                               G_PARAM_CONSTRUCT_ONLY |
+                               G_PARAM_STATIC_NAME |
+                               G_PARAM_STATIC_NICK |
+                               G_PARAM_STATIC_BLURB));
+  g_object_class_install_property (gobject_class, PROP_THEME, pspec);
+}
+
+/* Public API */
+
+/**
+ * blocks_cache_new:
+ *
+ * Returns: a new #BlocksCache object
+ */
+BlocksCache *
+blocks_cache_new (void)
+{
+  return static_cast<BlocksCache*>(g_object_new (TYPE_BLOCKS_CACHE, NULL));
+}
+
+/**
+ * blocks_cache_set_theme:
+ * @cache:
+ * @theme:
+ *
+ * Sets the block theme.
+ */
+void
+blocks_cache_set_theme (BlocksCache *cache,
+                        guint theme)
+{
+  BlocksCachePrivate *priv = cache->priv;
+
+  g_return_if_fail (IS_BLOCKS_CACHE (cache));
+
+  if (priv->theme == theme)
+    return;
+
+  blocks_cache_clear (cache);
+  blocks_cache_unset_theme (cache);
+
+  priv->theme = theme;
+  //priv->theme_changed_handler = g_signal_connect_swapped (GUINT_TO_POINTER(theme), "changed",
+  //                                                        G_CALLBACK (blocks_cache_clear),
+  //                                                        cache);
+  g_object_notify (G_OBJECT (cache), "theme");
+}
+
+/**
+ * blocks_cache_get_theme:
+ * @cache:
+ *
+ * Returns: the the block theme of @cache
+ */
+guint
+blocks_cache_get_theme (BlocksCache *cache)
+{
+  g_return_val_if_fail (IS_BLOCKS_CACHE (cache), NULL);
+
+  return cache->priv->theme;
+}
+
+/**
+ * blocks_cache_get_block_texture_by_id:
+ * @cache:
+ * @colour:
+ *
+ * Returns: a cached #CoglHandle for @colour.
+ */
+CoglHandle
+blocks_cache_get_block_texture_by_id (BlocksCache *cache,
+                                      guint colour)
+{
+  BlocksCachePrivate *priv = cache->priv;
+  CoglHandle handle;
+
+  g_return_val_if_fail (colour < NCOLOURS , NULL);
+
+  LOG_CALL (cache);
+
+  handle = priv->colours[colour];
+  if (IS_FAILED_HANDLE (handle)) {
+    LOG_CACHE_HIT (cache);
+    return COGL_INVALID_HANDLE;
+  }
+
+  if (handle == COGL_INVALID_HANDLE) {
+    cairo_surface_t *imgbuf = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                                          64, 64); /*FIXME for pixel-level precision*/
+
+    LOG_CACHE_MISS (cache);
+
+    Renderer *renderer = rendererFactory (priv->theme);
+    cairo_t *cr = cairo_create (imgbuf);
+
+    if (!cr) {
+      priv->colours[colour] = FAILED_HANDLE;
+      return COGL_INVALID_HANDLE;
+    }
+
+    renderer->drawCell (cr, colour);
+    cairo_destroy (cr);
+
+    handle = cogl_texture_new_from_data (64,
+                                         64,
+                                         COGL_TEXTURE_NONE,
+                                         COGL_PIXEL_FORMAT_RGBA_8888,
+                                         COGL_PIXEL_FORMAT_ANY,
+                                         2048,
+                                         cairo_image_surface_get_data (imgbuf));
+    cairo_surface_destroy (imgbuf);
+
+    if (handle == COGL_INVALID_HANDLE) {
+      priv->colours[colour] = FAILED_HANDLE;
+      return COGL_INVALID_HANDLE;
+    }
+
+    priv->colours[colour] = handle;
+  } else {
+    LOG_CACHE_HIT (cache);
+  }
+
+  return handle;
+}
+
diff --git a/gnometris/blocks-cache.h b/gnometris/blocks-cache.h
new file mode 100644
index 0000000..491213e
--- /dev/null
+++ b/gnometris/blocks-cache.h
@@ -0,0 +1,65 @@
+/*
+  Copyright © 2008 Neil Roberts
+  Copyright © 2008 Christian Persch
+  Copyright © 2009 Jason D. Clinton
+
+  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 BLOCKS_CACHE_H
+#define BLOCKS_CACHE_H
+
+#include <glib-object.h>
+#include <cogl/cogl.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_BLOCKS_CACHE            (blocks_cache_get_type())
+#define BLOCKS_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_BLOCKS_CACHE, BlocksCache))
+#define BLOCKS_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_BLOCKS_CACHE, BlocksCacheClass))
+#define IS_BLOCKS_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_BLOCKS_CACHE))
+#define IS_BLOCKS_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_BLOCKS_CACHE))
+#define BLOCKS_CACHE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_BLOCKS_CACHE, BlocksCacheClass))
+
+typedef struct _BlocksCache        BlocksCache;
+typedef struct _BlocksCacheClass   BlocksCacheClass;
+typedef struct _BlocksCachePrivate BlocksCachePrivate;
+
+struct _BlocksCacheClass
+{
+  GObjectClass parent_class;
+};
+
+struct _BlocksCache
+{
+  GObject parent;
+
+  BlocksCachePrivate *priv;
+};
+
+GType blocks_cache_get_type (void);
+
+BlocksCache *blocks_cache_new (void);
+
+void blocks_cache_set_theme (BlocksCache *cache,
+                             guint theme);
+
+guint blocks_cache_get_theme (BlocksCache *cache);
+
+CoglHandle blocks_cache_get_block_texture_by_id (BlocksCache *cache,
+                                                 guint colour);
+
+G_END_DECLS
+
+#endif /* BLOCKS_CACHE_H */
diff --git a/gnometris/blocks.cpp b/gnometris/blocks.cpp
index 123b365..f2afd23 100644
--- a/gnometris/blocks.cpp
+++ b/gnometris/blocks.cpp
@@ -19,6 +19,78 @@
  * For more details see the file COPYING.
  */
 
+#include "blocks.h"
+#include "blockops.h"
+
+Block::Block ():
+	what(EMPTY),
+	actor(NULL),
+	x(0),
+	y(0),
+	move_behaviour(NULL),
+	fall_behaviour(NULL),
+	explode_move_behaviour(NULL)
+{}
+
+Block::~Block ()
+{
+	if (actor)
+		clutter_actor_destroy (CLUTTER_ACTOR(actor));
+	if (move_behaviour)
+		g_object_unref (move_behaviour);
+	if (fall_behaviour)
+		g_object_unref (fall_behaviour);
+	if (explode_move_behaviour)
+		g_object_unref (explode_move_behaviour);
+}
+
+void
+Block::createActor (ClutterActor *chamber, CoglHandle texture_source, gint pxwidth, gint pxheight)
+{
+	if (actor)
+		clutter_actor_destroy (CLUTTER_ACTOR(actor));
+	actor = clutter_texture_new ();
+	clutter_texture_set_cogl_texture (CLUTTER_TEXTURE(actor),
+	                                  texture_source);
+	clutter_group_add (CLUTTER_GROUP (chamber), actor);
+	clutter_actor_set_position (CLUTTER_ACTOR(actor), x, y);
+	clutter_actor_show (CLUTTER_ACTOR(actor));
+}
+
+void
+Block::bindAnimations (BlockOps *f)
+{
+	move_behaviour = clutter_behaviour_path_new_with_knots (f->move_alpha,
+								NULL, 0);
+
+	fall_behaviour = clutter_behaviour_path_new_with_knots (f->fall_alpha,
+								NULL, 0);
+
+	explode_move_behaviour = clutter_behaviour_path_new_with_knots (f->explode_alpha,
+									NULL, 0);
+}
+
+Block&
+Block::moveFrom (Block& b, BlockOps *f)
+{
+	if (this != &b) {
+		what = b.what;
+		b.what = EMPTY;
+		color = b.color;
+		b.color = 0;
+		if (b.actor) {
+			const ClutterKnot knot_line[] = {{b.x, b.y}, {x, y}};
+			fall_behaviour = clutter_behaviour_path_new_with_knots (f->fall_alpha,
+										knot_line, 2);
+			clutter_behaviour_apply (fall_behaviour, b.actor);
+			f->fall_behaviours = g_list_prepend (f->fall_behaviours, fall_behaviour);
+		}
+		actor = b.actor;
+		b.actor = NULL;
+	}
+	return *this;
+}
+
 int blockTable[][4][4][4] =
 {
   {
diff --git a/gnometris/blocks.h b/gnometris/blocks.h
index 8c154b6..7b84dab 100644
--- a/gnometris/blocks.h
+++ b/gnometris/blocks.h
@@ -22,8 +22,43 @@
  * For more details see the file COPYING.
  */
 
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+class BlockOps;
+
 #define NCOLOURS 7
 
+enum SlotType {
+	EMPTY,
+	FALLING,
+	LAYING
+};
+
+class Block {
+public:
+	Block ();
+	~Block ();
+
+	Block& moveFrom (Block &b, BlockOps *f);
+
+	SlotType what;
+	guint color;
+	ClutterActor *actor;
+
+	int x;
+	int y;
+
+	void createActor (ClutterActor *chamber, CoglHandle texture_source, gint pxwidth, gint pxheight);
+	void bindAnimations (BlockOps *f);
+
+	/* Every block will have a unique position
+	 * These can be continuously cleared and repopulated with new paths */
+	ClutterBehaviour *move_behaviour;
+	ClutterBehaviour *fall_behaviour;
+	ClutterBehaviour *explode_move_behaviour;
+};
+
 extern int blockTable[][4][4][4];
 extern int tableSize;
 
diff --git a/gnometris/preview.cpp b/gnometris/preview.cpp
index 03f6c22..1d96f34 100644
--- a/gnometris/preview.cpp
+++ b/gnometris/preview.cpp
@@ -20,7 +20,6 @@
  * For more details see the file COPYING.
  */
 
-#include <config.h>
 #include "preview.h"
 
 #define PREVIEW_WIDTH 6
@@ -35,7 +34,7 @@ Preview::Preview():
 	blocknr(-1),
 	color(-1),
 	themeID(-1),
-	renderer(NULL),
+	cache(NULL),
 	enabled(true)
 {
 	blocks = new Block*[PREVIEW_WIDTH];
@@ -61,7 +60,6 @@ Preview::Preview():
 	piece = clutter_group_new ();
 	clutter_group_add (CLUTTER_GROUP (stage),
 			   piece);
-	clutter_actor_show_all (stage);
 
 	piece_timeline = clutter_timeline_new (180);
 	alpha = clutter_alpha_new_full (piece_timeline,
@@ -79,7 +77,7 @@ Preview::~Preview ()
 		delete[] blocks[i];
 
 	delete[] blocks;
-	delete renderer;
+	g_object_unref (cache);
 }
 
 void
@@ -91,27 +89,12 @@ Preview::enable(bool en)
 void
 Preview::setTheme (gint id)
 {
-	if (themeID == id)
-		return;
-
 	themeID = id;
 
-	if (renderer) {
-		delete renderer;
-		renderer = rendererFactory (themeID, PREVIEW_SIZE*4, PREVIEW_SIZE*4);
-	} else {
-		renderer = rendererFactory (themeID, PREVIEW_SIZE*4, PREVIEW_SIZE*4);
-	}
-}
+	if (cache)
+		g_object_unref (cache);
+	cache = blocks_cache_new ();
 
-void
-Preview::regenerateRenderer ()
-{
-	if (renderer)
-		renderer->rescaleCache (PREVIEW_SIZE*4, PREVIEW_SIZE*4);
-	else {
-		renderer = rendererFactory (themeID, PREVIEW_SIZE*4, PREVIEW_SIZE*4);
-	}
 }
 
 void
@@ -131,20 +114,21 @@ Preview::previewBlock(gint bnr, gint bcol)
 			    blockTable[blocknr][0][x-1][y-1]) {
 				blocks[x][y].what = LAYING;
 				blocks[x][y].createActor (piece,
-							  renderer->getCacheCellById (color));
+				                          blocks_cache_get_block_texture_by_id (cache, color),
+				                          PREVIEW_SIZE*4,
+				                          PREVIEW_SIZE*4);
 				clutter_actor_set_position (CLUTTER_ACTOR(blocks[x][y].actor),
-							    x*PREVIEW_SIZE*4, y*PREVIEW_SIZE*4);
+				                            x*PREVIEW_SIZE*4, y*PREVIEW_SIZE*4);
 			} else {
 				blocks[x][y].what = EMPTY;
 				if (blocks[x][y].actor) {
-					clutter_actor_destroy (blocks[x][y].actor);
+					clutter_actor_destroy (CLUTTER_ACTOR(blocks[x][y].actor));
 					blocks[x][y].actor = NULL;
 				}
 			}
 		}
 	}
 	clutter_timeline_start (piece_timeline);
-	clutter_actor_show_all (stage);
 }
 
 gint
@@ -152,7 +136,6 @@ Preview::resize(GtkWidget *widget, GtkAllocation *allocation, Preview *preview)
 {
 	preview->width = allocation->width;
 	preview->height = allocation->height;
-	preview->regenerateRenderer ();
 	clutter_actor_set_anchor_point (preview->piece, PREVIEW_SIZE*10, PREVIEW_SIZE*10);
 	clutter_actor_set_position (CLUTTER_ACTOR(preview->piece), PREVIEW_SIZE*10, PREVIEW_SIZE*10);
 	preview->previewBlock (preview->blocknr, preview->color);
diff --git a/gnometris/preview.h b/gnometris/preview.h
index 60d16b9..8a66c6c 100644
--- a/gnometris/preview.h
+++ b/gnometris/preview.h
@@ -23,11 +23,8 @@
  */
 
 #include "tetris.h"
-#include "blockops.h"
-#include "renderer.h"
 #include "blocks.h"
-
-#include <clutter/clutter.h>
+#include "blocks-cache.h"
 #include <clutter-gtk/clutter-gtk.h>
 
 class Preview {
@@ -42,7 +39,6 @@ public:
 	void enable (bool enable);
 	void setTheme (gint id);
 	void previewBlock (int bnr, int bcolor);
-	void regenerateRenderer ();
 
 private:
 	GtkWidget *w;
@@ -58,7 +54,7 @@ private:
 
 	Block **blocks;
 	ClutterActor* piece;
-	Renderer* renderer;
+	BlocksCache *cache;
 
 	bool enabled;
 
diff --git a/gnometris/renderer.cpp b/gnometris/renderer.cpp
index ccbc29c..3fe63ea 100644
--- a/gnometris/renderer.cpp
+++ b/gnometris/renderer.cpp
@@ -22,8 +22,8 @@
  */
 
 #include <config.h>
-
 #include <string.h>
+#include <glib/gi18n.h>
 
 #include "renderer.h"
 
@@ -53,87 +53,27 @@ gint themeNameToNumber (const gchar *id)
 	return 0;
 }
 
-Renderer * rendererFactory (gint id, gint pxw, gint pxh)
+Renderer * rendererFactory (gint id)
 {
+	Renderer *r;
 	switch (id) {
 	case 2:
-		return new TangoBlock (pxw, pxh, TRUE);
+		r = new TangoBlock (TRUE);
 	case 1:
-		return new TangoBlock (pxw, pxh, FALSE);
+		r = new TangoBlock (FALSE);
 	case 0:
 	default:
-		return new Renderer (pxw, pxh, FALSE);
+		r = new Renderer ();
 	}
+	return r;
 }
 
 /* The Renderer class is a basic drawing class that is structured to
-   be easily customised by subclasses. The most basic customisation
-   would be to override drawCell to customise the drawing of one
-   cell. If more sophisticated drawing is required for either the
-   foreground or the background is required then drawForeground and
-   drawBackground are the functions to alter. If a completely
-   different drawing regime is required then the render method - the
-   only entry point from external code - can be replaced. */
+   be easily customised by subclasses. Override drawCell to customise
+   the drawing of one cell. */
 
 /* Note that the default renderer is designed to be reasonably fast
-   and flexible, not flashy. Also note that the renderer may be used
-   for the preview widget and possibly the theme previewer, so make no
-   assumptions. */
-
-Renderer::Renderer (gint pxw, gint pxh, bool initFromSubclass)
-{
-	pxwidth = pxw == 0 ? 1 : pxw;
-	pxheight = pxh == 0 ? 1 : pxh;
-	for (int i = 0; i < NCOLOURS; i++) {
-		cache[i] = NULL;
-	}
-	if (!initFromSubclass)
-		rescaleCache (pxwidth, pxheight);
-}
-
-Renderer::~Renderer ()
-{
-	for (int i = 0; i<NCOLOURS; i++) {
-		if (cache[i])
-			cairo_surface_destroy (cache[i]);
-	}
-}
-
-cairo_surface_t* Renderer::getCacheCellById (gint id)
-{
-	return cache[id];
-}
-
-void Renderer::rescaleCache (gint x, gint y)
-{
-	if (x == 0 or y == 0)
-		return;
-
-	pxwidth = x;
-	pxheight = y;
-
-	if(cache[0]) {
-		for (int i = 0; i<NCOLOURS; i++) {
-			g_object_unref (cache[i]);
-			cache[i] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-							       pxwidth,
-							       pxheight);
-			cairo_t *cr = cairo_create (cache[i]);
-			cairo_scale (cr, 1.0 * pxwidth, 1.0 * pxheight);
-			drawCell (cr, i);
-			cairo_destroy (cr);
-		}
-	} else {
-		for (int i = 0; i<NCOLOURS; i++) {
-			cache[i] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-							       pxwidth, pxheight);
-			cairo_t *cr = cairo_create (cache[i]);
-			cairo_scale (cr, 1.0 * pxwidth, 1.0 * pxheight);
-			drawCell (cr, i);
-			cairo_destroy (cr);
-		}
-	}
-}
+   and flexible, not flashy. */
 
 void Renderer::drawCell (cairo_t *cr, guint color)
 {
@@ -153,10 +93,9 @@ void Renderer::drawCell (cairo_t *cr, guint color)
 	cairo_paint (cr);
 }
 
-TangoBlock::TangoBlock (gint pxw, gint pxh, gboolean grad) : Renderer (pxw, pxh, TRUE)
+TangoBlock::TangoBlock (gboolean grad) : Renderer ()
 {
 	usegrads = grad;
-	rescaleCache (pxwidth, pxheight);
 }
 
 void TangoBlock::drawCell (cairo_t *cr, guint color)
diff --git a/gnometris/renderer.h b/gnometris/renderer.h
index 920daea..8341d40 100644
--- a/gnometris/renderer.h
+++ b/gnometris/renderer.h
@@ -28,7 +28,6 @@
 #include <glib.h>
 
 #include "blocks.h"
-#include "tetris.h"
 
 struct ThemeTableEntry {
 	const gchar *name;
@@ -36,35 +35,25 @@ struct ThemeTableEntry {
 };
 
 extern const ThemeTableEntry ThemeTable[];
+gint themeNameToNumber (const gchar *id);
 
 class Renderer {
 public:
-	Renderer (gint pxw, gint pxh, bool initFromSubclass);
-	virtual ~ Renderer ();
-
-	void rescaleCache (gint pxw, gint pxh);
-	cairo_surface_t* getCacheCellById (gint id);
-
-	gint pxwidth;
-	gint pxheight;
-protected:
-	cairo_surface_t* cache[NCOLOURS];
-	virtual void drawCell (cairo_t * cr, guint color);
+	virtual void drawCell (cairo_t *cr, guint color);
 };
 
-Renderer *rendererFactory (gint id, gint pxw, gint pxh);
-gint themeNameToNumber (const gchar * id);
+Renderer *rendererFactory (gint id);
 
 class TangoBlock:public Renderer {
 public:
-	TangoBlock (gint pxw, gint pxh, gboolean grad);
+	TangoBlock (gboolean grad);
+	virtual void drawCell (cairo_t *cr, guint color);
 
-protected:
-	virtual void drawCell (cairo_t * cr, guint color);
+protected:	
 	gboolean usegrads;
 
 private:
-	void drawRoundedRectangle (cairo_t * cr, gdouble x, gdouble y, gdouble w, gdouble h, gdouble r);
+	void drawRoundedRectangle (cairo_t *cr, gdouble x, gdouble y, gdouble w, gdouble h, gdouble r);
 };
 
 #endif // __renderer_h__
diff --git a/gnometris/tetris.cpp b/gnometris/tetris.cpp
index 3fa92fe..d9e624f 100644
--- a/gnometris/tetris.cpp
+++ b/gnometris/tetris.cpp
@@ -26,8 +26,6 @@
 #include <math.h>
 #include <ctype.h>
 
-#include <glib/gi18n.h>
-#include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 #include <gio/gio.h>
 
@@ -43,8 +41,8 @@
 #include "scoreframe.h"
 #include "highscores.h"
 #include "preview.h"
-#include "renderer.h"
 #include "blockops.h"
+#include "renderer.h"
 
 int LINES = 20;
 int COLUMNS = 14;
diff --git a/gnometris/tetris.h b/gnometris/tetris.h
index 6690a3d..30f214f 100644
--- a/gnometris/tetris.h
+++ b/gnometris/tetris.h
@@ -26,7 +26,7 @@
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
+//#include <gdk-pixbuf/gdk-pixbuf.h>
 
 #include <libgames-support/games-conf.h>
 
diff --git a/libgames-support/games-debug.c b/libgames-support/games-debug.c
index 46b60dc..a424e8a 100644
--- a/libgames-support/games-debug.c
+++ b/libgames-support/games-debug.c
@@ -33,6 +33,7 @@ _games_debug_init (void)
   const GDebugKey keys[] = {
     { "card-theme",   GAMES_DEBUG_CARD_THEME   },
     { "card-cache",   GAMES_DEBUG_CARD_CACHE   },
+    { "blocks-cache", GAMES_DEBUG_BLOCKS_CACHE },
     { "runtime",      GAMES_DEBUG_RUNTIME      },
     { "sound",        GAMES_DEBUG_SOUND        },
     { "window-state", GAMES_DEBUG_WINDOW_STATE }
diff --git a/libgames-support/games-debug.h b/libgames-support/games-debug.h
index 244aeeb..bd1dcce 100644
--- a/libgames-support/games-debug.h
+++ b/libgames-support/games-debug.h
@@ -30,9 +30,10 @@ G_BEGIN_DECLS
 typedef enum {
   GAMES_DEBUG_CARD_THEME    = 1 << 0,
   GAMES_DEBUG_CARD_CACHE    = 1 << 1,
-  GAMES_DEBUG_RUNTIME       = 1 << 2,
-  GAMES_DEBUG_SOUND         = 1 << 3,
-  GAMES_DEBUG_WINDOW_STATE  = 1 << 4
+  GAMES_DEBUG_BLOCKS_CACHE  = 1 << 2,
+  GAMES_DEBUG_RUNTIME       = 1 << 3,
+  GAMES_DEBUG_SOUND         = 1 << 4,
+  GAMES_DEBUG_WINDOW_STATE  = 1 << 5
 } GamesDebugFlags;
 
 #ifdef GNOME_ENABLE_DEBUG



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