451 lines
11 KiB
C
451 lines
11 KiB
C
|
/*
|
||
|
* st-widget.h: Base class for St actors
|
||
|
*
|
||
|
* Copyright 2007 OpenedHand
|
||
|
* Copyright 2009 Intel Corporation.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
* under the terms and conditions of the GNU Lesser General Public License,
|
||
|
* version 2.1, as published by the Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||
|
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||
|
* more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public License
|
||
|
* along with this program; if not, write to the Free Software Foundation,
|
||
|
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
* Boston, MA 02111-1307, USA.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* SECTION:st-texture-cache
|
||
|
* @short_description: A per-process store to cache textures
|
||
|
*
|
||
|
* #StTextureCache allows an application to re-use an previously loaded
|
||
|
* textures.
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
|
||
|
#include <glib.h>
|
||
|
#include <glib-object.h>
|
||
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "st-texture-cache.h"
|
||
|
#include "st-marshal.h"
|
||
|
#include "st-private.h"
|
||
|
#include "st-subtexture.h"
|
||
|
G_DEFINE_TYPE (StTextureCache, st_texture_cache, G_TYPE_OBJECT)
|
||
|
|
||
|
#define TEXTURE_CACHE_PRIVATE(o) \
|
||
|
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_TEXTURE_CACHE, StTextureCachePrivate))
|
||
|
|
||
|
typedef struct _StTextureCachePrivate StTextureCachePrivate;
|
||
|
|
||
|
struct _StTextureCachePrivate
|
||
|
{
|
||
|
GHashTable *cache;
|
||
|
};
|
||
|
|
||
|
typedef struct FinalizedClosure
|
||
|
{
|
||
|
gchar *path;
|
||
|
StTextureCache *cache;
|
||
|
} FinalizedClosure;
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
PROP_0,
|
||
|
};
|
||
|
|
||
|
static StTextureCache* __cache_singleton = NULL;
|
||
|
|
||
|
/*
|
||
|
* Convention: posX with a value of -1 indicates whole texture
|
||
|
*/
|
||
|
typedef struct StTextureCacheItem {
|
||
|
char filename[256];
|
||
|
int width, height;
|
||
|
int posX, posY;
|
||
|
ClutterActor *ptr;
|
||
|
} StTextureCacheItem;
|
||
|
|
||
|
static StTextureCacheItem *
|
||
|
st_texture_cache_item_new (void)
|
||
|
{
|
||
|
return g_slice_new0 (StTextureCacheItem);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
st_texture_cache_item_free (StTextureCacheItem *item)
|
||
|
{
|
||
|
g_slice_free (StTextureCacheItem, item);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
st_texture_cache_set_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
switch (prop_id)
|
||
|
{
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
st_texture_cache_get_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
switch (prop_id)
|
||
|
{
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
st_texture_cache_dispose (GObject *object)
|
||
|
{
|
||
|
if (G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose)
|
||
|
G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
st_texture_cache_finalize (GObject *object)
|
||
|
{
|
||
|
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(object);
|
||
|
|
||
|
if (priv->cache)
|
||
|
{
|
||
|
g_hash_table_unref (priv->cache);
|
||
|
priv->cache = NULL;
|
||
|
}
|
||
|
|
||
|
G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
st_texture_cache_class_init (StTextureCacheClass *klass)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
|
||
|
g_type_class_add_private (klass, sizeof (StTextureCachePrivate));
|
||
|
|
||
|
object_class->get_property = st_texture_cache_get_property;
|
||
|
object_class->set_property = st_texture_cache_set_property;
|
||
|
object_class->dispose = st_texture_cache_dispose;
|
||
|
object_class->finalize = st_texture_cache_finalize;
|
||
|
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
st_texture_cache_init (StTextureCache *self)
|
||
|
{
|
||
|
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
|
||
|
|
||
|
priv->cache = g_hash_table_new_full (g_str_hash,
|
||
|
g_str_equal,
|
||
|
g_free,
|
||
|
NULL);
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* st_texture_cache_get_default:
|
||
|
*
|
||
|
* Returns the default texture cache. This is owned by St and should not be
|
||
|
* unreferenced or freed.
|
||
|
*
|
||
|
* Returns: a StTextureCache
|
||
|
*/
|
||
|
StTextureCache*
|
||
|
st_texture_cache_get_default (void)
|
||
|
{
|
||
|
if (G_UNLIKELY (__cache_singleton == NULL))
|
||
|
__cache_singleton = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
|
||
|
|
||
|
return __cache_singleton;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
static void
|
||
|
on_texure_finalized (gpointer data,
|
||
|
GObject *where_the_object_was)
|
||
|
{
|
||
|
FinalizedClosure *closure = (FinalizedClosure *) data;
|
||
|
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(closure->cache);
|
||
|
|
||
|
g_hash_table_remove (priv->cache, closure->path);
|
||
|
|
||
|
g_free(closure->path);
|
||
|
g_free(closure);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* st_texture_cache_get_size:
|
||
|
* @self: A #StTextureCache
|
||
|
*
|
||
|
* Returns the number of items in the texture cache
|
||
|
*
|
||
|
* Returns: the current size of the cache
|
||
|
*/
|
||
|
gint
|
||
|
st_texture_cache_get_size (StTextureCache *self)
|
||
|
{
|
||
|
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
|
||
|
|
||
|
return g_hash_table_size (priv->cache);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
add_texture_to_cache (StTextureCache *self,
|
||
|
const gchar *path,
|
||
|
StTextureCacheItem *item)
|
||
|
{
|
||
|
/* FinalizedClosure *closure; */
|
||
|
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
|
||
|
|
||
|
g_hash_table_insert (priv->cache, g_strdup (path), item);
|
||
|
|
||
|
#if 0
|
||
|
/* Make sure we can remove from hash */
|
||
|
closure = g_new0 (FinalizedClosure, 1);
|
||
|
closure->path = g_strdup (path);
|
||
|
closure->cache = self;
|
||
|
|
||
|
g_object_weak_ref (G_OBJECT (res), on_texure_finalized, closure);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* NOTE: you should unref the returned texture when not needed */
|
||
|
|
||
|
/**
|
||
|
* st_texture_cache_get_texture:
|
||
|
* @self: A #StTextureCache
|
||
|
* @path: A path to a image file
|
||
|
*
|
||
|
* Create a new ClutterTexture with the specified image. Adds the image to the
|
||
|
* cache if the image had not been previously loaded. Subsequent calls with
|
||
|
* the same image path will return a new ClutterTexture with the previously
|
||
|
* loaded image.
|
||
|
*
|
||
|
* Returns: a newly created ClutterTexture
|
||
|
*/
|
||
|
ClutterTexture*
|
||
|
st_texture_cache_get_texture (StTextureCache *self,
|
||
|
const gchar *path)
|
||
|
{
|
||
|
ClutterActor *texture;
|
||
|
CoglHandle *handle;
|
||
|
StTextureCachePrivate *priv;
|
||
|
StTextureCacheItem *item;
|
||
|
|
||
|
g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
|
||
|
g_return_val_if_fail (path != NULL, NULL);
|
||
|
|
||
|
|
||
|
priv = TEXTURE_CACHE_PRIVATE (self);
|
||
|
|
||
|
item = g_hash_table_lookup (priv->cache, path);
|
||
|
|
||
|
if (item && item->posX != -1)
|
||
|
{
|
||
|
GError *err = NULL;
|
||
|
/*
|
||
|
* We have a cache hit, but it's for a partial texture. The only
|
||
|
* sane option is to read it from disk and just don't cache it
|
||
|
* at all.
|
||
|
*/
|
||
|
return CLUTTER_TEXTURE(clutter_texture_new_from_file(path, &err));
|
||
|
}
|
||
|
if (!item)
|
||
|
{
|
||
|
GError *err = NULL;
|
||
|
|
||
|
item = st_texture_cache_item_new ();
|
||
|
item->posX = -1;
|
||
|
item->posY = -1;
|
||
|
item->ptr = clutter_texture_new_from_file (path, &err);
|
||
|
clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
|
||
|
&item->width, &item->height);
|
||
|
|
||
|
if (!item->ptr)
|
||
|
{
|
||
|
if (err)
|
||
|
{
|
||
|
g_warning ("Error loading image: %s", err->message);
|
||
|
g_error_free (err);
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
add_texture_to_cache (self, path, item);
|
||
|
}
|
||
|
|
||
|
texture = clutter_texture_new ();
|
||
|
handle = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (item->ptr));
|
||
|
clutter_texture_set_cogl_texture ((ClutterTexture*) texture, handle);
|
||
|
|
||
|
return (ClutterTexture*) texture;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* st_texture_cache_get_actor:
|
||
|
* @self: A #StTextureCache
|
||
|
* @path: A path to a image file
|
||
|
*
|
||
|
* Create a new ClutterSubTexture with the specified image. Adds the image to the
|
||
|
* cache if the image had not been previously loaded. Subsequent calls with
|
||
|
* the same image path will return a new ClutterTexture with the previously
|
||
|
* loaded image.
|
||
|
*
|
||
|
* Use this function if all you need is an actor for drawing.
|
||
|
*
|
||
|
* Returns: a newly created ClutterTexture
|
||
|
*/
|
||
|
ClutterActor*
|
||
|
st_texture_cache_get_actor (StTextureCache *self,
|
||
|
const gchar *path)
|
||
|
{
|
||
|
StTextureCachePrivate *priv;
|
||
|
StTextureCacheItem *item;
|
||
|
GError *err = NULL;
|
||
|
|
||
|
g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
|
||
|
g_return_val_if_fail (path != NULL, NULL);
|
||
|
|
||
|
priv = TEXTURE_CACHE_PRIVATE (self);
|
||
|
|
||
|
|
||
|
item = g_hash_table_lookup (priv->cache, path);
|
||
|
|
||
|
if (item)
|
||
|
{
|
||
|
int posX = item->posX;
|
||
|
int posY = item->posY;
|
||
|
if (posX == -1)
|
||
|
posX = 0;
|
||
|
if (posY == -1)
|
||
|
posY = 0;
|
||
|
return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), posX, posY,
|
||
|
item->width, item->height);
|
||
|
}
|
||
|
|
||
|
item = st_texture_cache_item_new ();
|
||
|
item->posX = -1;
|
||
|
item->posY = -1;
|
||
|
item->ptr = clutter_texture_new_from_file (path, &err);
|
||
|
clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
|
||
|
&item->width, &item->height);
|
||
|
|
||
|
if (!item->ptr)
|
||
|
{
|
||
|
if (err)
|
||
|
{
|
||
|
g_warning ("Error loading image: %s", err->message);
|
||
|
g_error_free (err);
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
add_texture_to_cache (self, path, item);
|
||
|
|
||
|
return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), 0, 0, item->width,
|
||
|
item->height);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
st_texture_cache_load_cache (StTextureCache *self,
|
||
|
const gchar *filename)
|
||
|
{
|
||
|
FILE *file;
|
||
|
StTextureCacheItem *element, head;
|
||
|
int ret;
|
||
|
ClutterActor *actor;
|
||
|
GError *error = NULL;
|
||
|
StTextureCachePrivate *priv;
|
||
|
|
||
|
g_return_if_fail (ST_IS_TEXTURE_CACHE (self));
|
||
|
g_return_if_fail (filename != NULL);
|
||
|
|
||
|
priv = TEXTURE_CACHE_PRIVATE (self);
|
||
|
|
||
|
file = fopen(filename, "rm");
|
||
|
if (!file)
|
||
|
return;
|
||
|
|
||
|
ret = fread (&head, sizeof(StTextureCacheItem), 1, file);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
fclose (file);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* check if we already if this texture in the cache */
|
||
|
if (g_hash_table_lookup (priv->cache, head.filename))
|
||
|
{
|
||
|
/* skip it, we're done */
|
||
|
fclose (file);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
actor = clutter_texture_new_from_file (head.filename, &error);
|
||
|
|
||
|
if (error)
|
||
|
{
|
||
|
g_critical (G_STRLOC ": Error opening cache image file: %s",
|
||
|
error->message);
|
||
|
g_clear_error (&error);
|
||
|
fclose (file);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
element = st_texture_cache_item_new ();
|
||
|
element->posX = -1;
|
||
|
element->posY = -1;
|
||
|
element->ptr = actor;
|
||
|
strncpy (element->filename, head.filename, 256);
|
||
|
clutter_texture_get_base_size (CLUTTER_TEXTURE (element->ptr),
|
||
|
&element->width, &element->height);
|
||
|
g_hash_table_insert (priv->cache, element->filename, element);
|
||
|
|
||
|
while (!feof (file))
|
||
|
{
|
||
|
element = st_texture_cache_item_new ();
|
||
|
ret = fread (element, sizeof (StTextureCacheItem), 1, file);
|
||
|
if (ret < 1)
|
||
|
{
|
||
|
/* end of file */
|
||
|
st_texture_cache_item_free (element);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
element->ptr = actor;
|
||
|
|
||
|
if (g_hash_table_lookup (priv->cache, element->filename))
|
||
|
{
|
||
|
/* file is already in the cache.... */
|
||
|
st_texture_cache_item_free (element);
|
||
|
} else {
|
||
|
g_hash_table_insert (priv->cache, element->filename, element);
|
||
|
}
|
||
|
}
|
||
|
}
|