Import Mx core as ST
Import the core MxWidget/MxBin and their dependencies; we use the namespace "St" (Shell Toolkit) because it is the same length as Mx so enabling easy sharing of code, but makes it clear that this is a friendly fork and not a literal import. Based on a patch by Colin Walters <walters@verbum.org> https://bugzilla.gnome.org/show_bug.cgi?id=591245
This commit is contained in:
450
src/st/st-texture-cache.c
Normal file
450
src/st/st-texture-cache.c
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user