2009-09-29 15:08:01 -04:00
|
|
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
2010-11-10 17:00:45 -05:00
|
|
|
/*
|
|
|
|
* st-texture-cache.h: Object for loading and caching images as textures
|
|
|
|
*
|
|
|
|
* Copyright 2009, 2010 Red Hat, Inc.
|
|
|
|
* Copyright 2010, Maxim Ermilov
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
|
|
* published by the Free Software Foundation, either version 2.1 of
|
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2009-09-08 15:47:30 -04:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2019-02-26 21:18:39 -05:00
|
|
|
#include "st-image-content.h"
|
2010-02-09 12:42:07 -05:00
|
|
|
#include "st-texture-cache.h"
|
2012-08-26 09:36:45 -04:00
|
|
|
#include "st-private.h"
|
2018-11-27 16:07:25 -05:00
|
|
|
#include "st-settings.h"
|
2010-02-09 12:42:07 -05:00
|
|
|
#include <gtk/gtk.h>
|
2017-05-11 00:25:00 -04:00
|
|
|
#include <math.h>
|
2009-09-08 15:47:30 -04:00
|
|
|
#include <string.h>
|
2010-02-09 12:42:07 -05:00
|
|
|
#include <glib.h>
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2012-03-17 00:20:31 -04:00
|
|
|
#define CACHE_PREFIX_ICON "icon:"
|
2014-09-18 20:04:00 -04:00
|
|
|
#define CACHE_PREFIX_FILE "file:"
|
|
|
|
#define CACHE_PREFIX_FILE_FOR_CAIRO "file-for-cairo:"
|
2009-09-08 15:47:30 -04:00
|
|
|
|
|
|
|
struct _StTextureCachePrivate
|
|
|
|
{
|
2010-11-12 19:15:09 -05:00
|
|
|
GtkIconTheme *icon_theme;
|
2018-11-27 16:07:25 -05:00
|
|
|
GSettings *settings;
|
2010-11-12 19:15:09 -05:00
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
/* Things that were loaded with a cache policy != NONE */
|
2019-01-24 10:42:14 -05:00
|
|
|
GHashTable *keyed_cache; /* char * -> ClutterImage* */
|
2018-04-17 05:53:02 -04:00
|
|
|
GHashTable *keyed_surface_cache; /* char * -> cairo_surface_t* */
|
2012-03-16 23:48:22 -04:00
|
|
|
|
2019-11-26 02:28:21 -05:00
|
|
|
GHashTable *used_scales; /* Set: double */
|
|
|
|
|
2012-03-16 23:48:22 -04:00
|
|
|
/* Presently this is used to de-duplicate requests for GIcons and async URIs. */
|
2010-02-09 15:12:28 -05:00
|
|
|
GHashTable *outstanding_requests; /* char * -> AsyncTextureLoadData * */
|
2012-09-20 15:42:52 -04:00
|
|
|
|
|
|
|
/* File monitors to evict cache data on changes */
|
|
|
|
GHashTable *file_monitors; /* char * -> GFileMonitor * */
|
2020-05-22 16:53:39 -04:00
|
|
|
|
|
|
|
GCancellable *cancellable;
|
2009-09-08 15:47:30 -04:00
|
|
|
};
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
static void st_texture_cache_dispose (GObject *object);
|
|
|
|
static void st_texture_cache_finalize (GObject *object);
|
|
|
|
|
2010-11-12 19:15:09 -05:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
ICON_THEME_CHANGED,
|
2012-09-20 15:42:52 -04:00
|
|
|
TEXTURE_FILE_CHANGED,
|
2010-11-12 19:15:09 -05:00
|
|
|
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0, };
|
2010-02-09 12:42:07 -05:00
|
|
|
G_DEFINE_TYPE(StTextureCache, st_texture_cache, G_TYPE_OBJECT);
|
|
|
|
|
|
|
|
/* We want to preserve the aspect ratio by default, also the default
|
2014-08-07 14:40:01 -04:00
|
|
|
* pipeline for an empty texture is full opacity white, which we
|
2010-02-09 12:42:07 -05:00
|
|
|
* definitely don't want. Skip that by setting 0 opacity.
|
|
|
|
*/
|
2019-01-24 10:42:14 -05:00
|
|
|
static ClutterActor *
|
|
|
|
create_invisible_actor (void)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
return g_object_new (CLUTTER_TYPE_ACTOR,
|
|
|
|
"opacity", 0,
|
2019-02-09 13:16:15 -05:00
|
|
|
"request-mode", CLUTTER_REQUEST_CONTENT_SIZE,
|
2019-01-24 10:42:14 -05:00
|
|
|
NULL);
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
/* Reverse the opacity we added while loading */
|
2009-09-08 15:47:30 -04:00
|
|
|
static void
|
2019-01-24 10:42:14 -05:00
|
|
|
set_content_from_image (ClutterActor *actor,
|
|
|
|
ClutterContent *image)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
g_assert (image && CLUTTER_IS_IMAGE (image));
|
|
|
|
|
|
|
|
clutter_actor_set_content (actor, image);
|
|
|
|
clutter_actor_set_opacity (actor, 255);
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-02-09 12:42:07 -05:00
|
|
|
st_texture_cache_class_init (StTextureCacheClass *klass)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2010-02-09 12:42:07 -05:00
|
|
|
GObjectClass *gobject_class = (GObjectClass *)klass;
|
|
|
|
|
|
|
|
gobject_class->dispose = st_texture_cache_dispose;
|
|
|
|
gobject_class->finalize = st_texture_cache_finalize;
|
2010-11-12 19:15:09 -05:00
|
|
|
|
2020-05-20 19:50:09 -04:00
|
|
|
/**
|
|
|
|
* StTextureCache::icon-theme-changed:
|
|
|
|
* @self: a #StTextureCache
|
|
|
|
*
|
|
|
|
* Emitted when the icon theme is changed.
|
|
|
|
*/
|
2010-11-12 19:15:09 -05:00
|
|
|
signals[ICON_THEME_CHANGED] =
|
|
|
|
g_signal_new ("icon-theme-changed",
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
0, /* no default handler slot */
|
2011-10-18 18:17:49 -04:00
|
|
|
NULL, NULL, NULL,
|
2010-11-12 19:15:09 -05:00
|
|
|
G_TYPE_NONE, 0);
|
2012-09-20 15:42:52 -04:00
|
|
|
|
2020-05-20 19:50:09 -04:00
|
|
|
/**
|
|
|
|
* StTextureCache::texture-file-changed:
|
|
|
|
* @self: a #StTextureCache
|
|
|
|
* @file: a #GFile
|
|
|
|
*
|
|
|
|
* Emitted when the source file of a texture is changed.
|
|
|
|
*/
|
2012-09-20 15:42:52 -04:00
|
|
|
signals[TEXTURE_FILE_CHANGED] =
|
|
|
|
g_signal_new ("texture-file-changed",
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
0, /* no default handler slot */
|
|
|
|
NULL, NULL, NULL,
|
2014-09-18 20:04:00 -04:00
|
|
|
G_TYPE_NONE, 1, G_TYPE_FILE);
|
2010-11-12 19:15:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Evicts all cached textures for named icons */
|
|
|
|
static void
|
|
|
|
st_texture_cache_evict_icons (StTextureCache *cache)
|
|
|
|
{
|
|
|
|
GHashTableIter iter;
|
|
|
|
gpointer key;
|
|
|
|
gpointer value;
|
|
|
|
|
|
|
|
g_hash_table_iter_init (&iter, cache->priv->keyed_cache);
|
|
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
|
|
{
|
|
|
|
const char *cache_key = key;
|
|
|
|
|
|
|
|
/* This is too conservative - it takes out all cached textures
|
|
|
|
* for GIcons even when they aren't named icons, but it's not
|
|
|
|
* worth the complexity of parsing the key and calling
|
|
|
|
* g_icon_new_for_string(); icon theme changes aren't normal */
|
2012-03-17 00:20:31 -04:00
|
|
|
if (g_str_has_prefix (cache_key, CACHE_PREFIX_ICON))
|
2010-11-12 19:15:09 -05:00
|
|
|
g_hash_table_iter_remove (&iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-11-27 16:07:25 -05:00
|
|
|
on_icon_theme_changed (StSettings *settings,
|
|
|
|
GParamSpec *pspec,
|
2010-11-12 19:15:09 -05:00
|
|
|
StTextureCache *cache)
|
|
|
|
{
|
2020-03-02 12:16:25 -05:00
|
|
|
g_autofree gchar *theme = NULL;
|
2018-11-27 16:07:25 -05:00
|
|
|
|
2020-05-22 16:53:39 -04:00
|
|
|
g_cancellable_cancel (cache->priv->cancellable);
|
|
|
|
g_cancellable_reset (cache->priv->cancellable);
|
|
|
|
|
2010-11-12 19:15:09 -05:00
|
|
|
st_texture_cache_evict_icons (cache);
|
2018-11-27 16:07:25 -05:00
|
|
|
|
|
|
|
g_object_get (settings, "gtk-icon-theme", &theme, NULL);
|
|
|
|
gtk_icon_theme_set_custom_theme (cache->priv->icon_theme, theme);
|
|
|
|
|
2010-11-12 19:15:09 -05:00
|
|
|
g_signal_emit (cache, signals[ICON_THEME_CHANGED], 0);
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
2019-08-01 19:54:42 -04:00
|
|
|
static void
|
|
|
|
on_gtk_icon_theme_changed (GtkIconTheme *icon_theme,
|
|
|
|
StTextureCache *self)
|
|
|
|
{
|
|
|
|
st_texture_cache_evict_icons (self);
|
|
|
|
g_signal_emit (self, signals[ICON_THEME_CHANGED], 0);
|
|
|
|
}
|
|
|
|
|
2009-09-08 15:47:30 -04:00
|
|
|
static void
|
2010-02-09 12:42:07 -05:00
|
|
|
st_texture_cache_init (StTextureCache *self)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2018-11-27 16:07:25 -05:00
|
|
|
StSettings *settings;
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
self->priv = g_new0 (StTextureCachePrivate, 1);
|
2010-11-12 19:15:09 -05:00
|
|
|
|
2018-11-27 16:07:25 -05:00
|
|
|
self->priv->icon_theme = gtk_icon_theme_new ();
|
|
|
|
gtk_icon_theme_add_resource_path (self->priv->icon_theme,
|
|
|
|
"/org/gnome/shell/theme/icons");
|
2019-08-01 19:54:42 -04:00
|
|
|
g_signal_connect (self->priv->icon_theme, "changed",
|
|
|
|
G_CALLBACK (on_gtk_icon_theme_changed), self);
|
2018-11-27 16:07:25 -05:00
|
|
|
|
|
|
|
settings = st_settings_get ();
|
|
|
|
g_signal_connect (settings, "notify::gtk-icon-theme",
|
2010-11-12 19:15:09 -05:00
|
|
|
G_CALLBACK (on_icon_theme_changed), self);
|
|
|
|
|
2010-02-09 15:12:28 -05:00
|
|
|
self->priv->keyed_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
|
2019-01-24 10:42:14 -05:00
|
|
|
g_free, g_object_unref);
|
2018-04-17 05:53:02 -04:00
|
|
|
self->priv->keyed_surface_cache = g_hash_table_new_full (g_str_hash,
|
|
|
|
g_str_equal,
|
|
|
|
g_free,
|
|
|
|
(GDestroyNotify) cairo_surface_destroy);
|
2020-02-20 16:51:38 -05:00
|
|
|
self->priv->used_scales = g_hash_table_new_full (g_double_hash, g_double_equal,
|
|
|
|
g_free, NULL);
|
2010-02-09 15:12:28 -05:00
|
|
|
self->priv->outstanding_requests = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
|
|
g_free, NULL);
|
2014-09-18 20:04:00 -04:00
|
|
|
self->priv->file_monitors = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
|
2012-09-20 15:42:52 -04:00
|
|
|
g_object_unref, g_object_unref);
|
|
|
|
|
2020-05-22 16:53:39 -04:00
|
|
|
self->priv->cancellable = g_cancellable_new ();
|
|
|
|
|
2018-11-27 16:07:25 -05:00
|
|
|
on_icon_theme_changed (settings, NULL, self);
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
st_texture_cache_dispose (GObject *object)
|
|
|
|
{
|
2010-02-09 12:42:07 -05:00
|
|
|
StTextureCache *self = (StTextureCache*)object;
|
|
|
|
|
2020-05-22 16:53:39 -04:00
|
|
|
g_cancellable_cancel (self->priv->cancellable);
|
|
|
|
|
2018-11-27 16:07:25 -05:00
|
|
|
g_clear_object (&self->priv->settings);
|
|
|
|
g_clear_object (&self->priv->icon_theme);
|
2020-05-22 16:53:39 -04:00
|
|
|
g_clear_object (&self->priv->cancellable);
|
2010-11-12 19:15:09 -05:00
|
|
|
|
2012-09-20 15:42:52 -04:00
|
|
|
g_clear_pointer (&self->priv->keyed_cache, g_hash_table_destroy);
|
2018-04-17 05:53:02 -04:00
|
|
|
g_clear_pointer (&self->priv->keyed_surface_cache, g_hash_table_destroy);
|
2019-11-26 02:28:21 -05:00
|
|
|
g_clear_pointer (&self->priv->used_scales, g_hash_table_destroy);
|
2012-09-20 15:42:52 -04:00
|
|
|
g_clear_pointer (&self->priv->outstanding_requests, g_hash_table_destroy);
|
|
|
|
g_clear_pointer (&self->priv->file_monitors, g_hash_table_destroy);
|
2010-02-09 15:12:28 -05:00
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
st_texture_cache_finalize (GObject *object)
|
|
|
|
{
|
2010-02-09 12:42:07 -05:00
|
|
|
G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
2014-03-22 23:26:09 -04:00
|
|
|
static void
|
2010-02-09 12:42:07 -05:00
|
|
|
compute_pixbuf_scale (gint width,
|
|
|
|
gint height,
|
|
|
|
gint available_width,
|
|
|
|
gint available_height,
|
|
|
|
gint *new_width,
|
|
|
|
gint *new_height)
|
|
|
|
{
|
|
|
|
int scaled_width, scaled_height;
|
|
|
|
|
|
|
|
if (width == 0 || height == 0)
|
2014-03-22 23:26:09 -04:00
|
|
|
{
|
|
|
|
*new_width = *new_height = 0;
|
|
|
|
return;
|
|
|
|
}
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
if (available_width >= 0 && available_height >= 0)
|
|
|
|
{
|
2011-10-18 21:21:10 -04:00
|
|
|
/* This should keep the aspect ratio of the image intact, because if
|
|
|
|
* available_width < (available_height * width) / height
|
|
|
|
* then
|
|
|
|
* (available_width * height) / width < available_height
|
|
|
|
* So we are guaranteed to either scale the image to have an available_width
|
|
|
|
* for width and height scaled accordingly OR have the available_height
|
|
|
|
* for height and width scaled accordingly, whichever scaling results
|
|
|
|
* in the image that can fit both available dimensions.
|
|
|
|
*/
|
2010-02-09 12:42:07 -05:00
|
|
|
scaled_width = MIN (available_width, (available_height * width) / height);
|
|
|
|
scaled_height = MIN (available_height, (available_width * height) / width);
|
|
|
|
}
|
|
|
|
else if (available_width >= 0)
|
|
|
|
{
|
|
|
|
scaled_width = available_width;
|
|
|
|
scaled_height = (available_width * height) / width;
|
|
|
|
}
|
|
|
|
else if (available_height >= 0)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2010-02-09 12:42:07 -05:00
|
|
|
scaled_width = (available_height * width) / height;
|
|
|
|
scaled_height = available_height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
scaled_width = scaled_height = 0;
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
2011-10-18 21:21:10 -04:00
|
|
|
/* Scale the image only if that will not increase its original dimensions. */
|
2010-02-09 12:42:07 -05:00
|
|
|
if (scaled_width > 0 && scaled_height > 0 && scaled_width < width && scaled_height < height)
|
|
|
|
{
|
|
|
|
*new_width = scaled_width;
|
|
|
|
*new_height = scaled_height;
|
|
|
|
}
|
2014-03-22 23:26:09 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
*new_width = width;
|
|
|
|
*new_height = height;
|
|
|
|
}
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
2010-11-02 16:12:56 -04:00
|
|
|
static void
|
|
|
|
rgba_from_clutter (GdkRGBA *rgba,
|
|
|
|
ClutterColor *color)
|
|
|
|
{
|
|
|
|
rgba->red = color->red / 255.;
|
|
|
|
rgba->green = color->green / 255.;
|
|
|
|
rgba->blue = color->blue / 255.;
|
|
|
|
rgba->alpha = color->alpha / 255.;
|
|
|
|
}
|
|
|
|
|
2014-03-22 23:29:23 -04:00
|
|
|
/* A private structure for keeping width, height and scale. */
|
2010-02-09 12:42:07 -05:00
|
|
|
typedef struct {
|
|
|
|
int width;
|
|
|
|
int height;
|
2014-03-22 23:29:23 -04:00
|
|
|
int scale;
|
2010-02-09 12:42:07 -05:00
|
|
|
} Dimensions;
|
|
|
|
|
2011-10-05 05:00:23 -04:00
|
|
|
/* This struct corresponds to a request for an texture.
|
|
|
|
* It's creasted when something needs a new texture,
|
|
|
|
* and destroyed when the texture data is loaded. */
|
|
|
|
typedef struct {
|
|
|
|
StTextureCache *cache;
|
|
|
|
StTextureCachePolicy policy;
|
|
|
|
char *key;
|
|
|
|
|
|
|
|
guint width;
|
|
|
|
guint height;
|
2017-05-11 00:25:00 -04:00
|
|
|
guint paint_scale;
|
|
|
|
gfloat resource_scale;
|
2019-01-24 10:42:14 -05:00
|
|
|
GSList *actors;
|
2011-10-05 05:00:23 -04:00
|
|
|
|
|
|
|
GtkIconInfo *icon_info;
|
|
|
|
StIconColors *colors;
|
2014-09-18 20:04:00 -04:00
|
|
|
GFile *file;
|
2011-10-05 05:00:23 -04:00
|
|
|
} AsyncTextureLoadData;
|
|
|
|
|
2009-09-08 15:47:30 -04:00
|
|
|
static void
|
2013-02-14 16:04:03 -05:00
|
|
|
texture_load_data_free (gpointer p)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2011-10-05 05:00:23 -04:00
|
|
|
AsyncTextureLoadData *data = p;
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2012-03-16 23:03:02 -04:00
|
|
|
if (data->icon_info)
|
2010-02-09 12:42:07 -05:00
|
|
|
{
|
2013-09-10 17:11:16 -04:00
|
|
|
g_object_unref (data->icon_info);
|
2011-10-05 05:00:23 -04:00
|
|
|
if (data->colors)
|
|
|
|
st_icon_colors_unref (data->colors);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
2014-09-18 20:04:00 -04:00
|
|
|
else if (data->file)
|
|
|
|
g_object_unref (data->file);
|
2011-10-05 05:00:23 -04:00
|
|
|
|
|
|
|
if (data->key)
|
|
|
|
g_free (data->key);
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
if (data->actors)
|
|
|
|
g_slist_free_full (data->actors, (GDestroyNotify) g_object_unref);
|
2013-02-14 16:04:03 -05:00
|
|
|
|
2020-10-19 14:12:27 -04:00
|
|
|
g_free (data);
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-02-09 12:42:07 -05:00
|
|
|
* on_image_size_prepared:
|
|
|
|
* @pixbuf_loader: #GdkPixbufLoader loading the image
|
|
|
|
* @width: the original width of the image
|
|
|
|
* @height: the original height of the image
|
2020-08-19 05:26:11 -04:00
|
|
|
* @data: pointer to the #Dimensions structure containing available width and height for the image,
|
2010-02-09 12:42:07 -05:00
|
|
|
* available width or height can be -1 if the dimension is not limited
|
2009-09-08 15:47:30 -04:00
|
|
|
*
|
2010-02-09 12:42:07 -05:00
|
|
|
* Private function.
|
|
|
|
*
|
|
|
|
* Sets the size of the image being loaded to fit the available width and height dimensions,
|
|
|
|
* but never scales up the image beyond its actual size.
|
|
|
|
* Intended to be used as a callback for #GdkPixbufLoader "size-prepared" signal.
|
2009-09-08 15:47:30 -04:00
|
|
|
*/
|
2010-02-09 12:42:07 -05:00
|
|
|
static void
|
|
|
|
on_image_size_prepared (GdkPixbufLoader *pixbuf_loader,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
Dimensions *available_dimensions = data;
|
|
|
|
int available_width = available_dimensions->width;
|
|
|
|
int available_height = available_dimensions->height;
|
2014-03-22 23:29:23 -04:00
|
|
|
int scale_factor = available_dimensions->scale;
|
2010-02-09 12:42:07 -05:00
|
|
|
int scaled_width;
|
|
|
|
int scaled_height;
|
|
|
|
|
2014-03-22 23:26:09 -04:00
|
|
|
compute_pixbuf_scale (width, height, available_width, available_height,
|
|
|
|
&scaled_width, &scaled_height);
|
|
|
|
|
|
|
|
gdk_pixbuf_loader_set_size (pixbuf_loader,
|
2014-03-22 23:29:23 -04:00
|
|
|
scaled_width * scale_factor,
|
|
|
|
scaled_height * scale_factor);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static GdkPixbuf *
|
|
|
|
impl_load_pixbuf_data (const guchar *data,
|
|
|
|
gsize size,
|
|
|
|
int available_width,
|
|
|
|
int available_height,
|
2014-03-22 23:29:23 -04:00
|
|
|
int scale,
|
2010-02-09 12:42:07 -05:00
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
GdkPixbufLoader *pixbuf_loader = NULL;
|
|
|
|
GdkPixbuf *rotated_pixbuf = NULL;
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
gboolean success;
|
|
|
|
Dimensions available_dimensions;
|
|
|
|
int width_before_rotation, width_after_rotation;
|
|
|
|
|
|
|
|
pixbuf_loader = gdk_pixbuf_loader_new ();
|
|
|
|
|
|
|
|
available_dimensions.width = available_width;
|
|
|
|
available_dimensions.height = available_height;
|
2014-03-22 23:29:23 -04:00
|
|
|
available_dimensions.scale = scale;
|
2010-02-09 12:42:07 -05:00
|
|
|
g_signal_connect (pixbuf_loader, "size-prepared",
|
|
|
|
G_CALLBACK (on_image_size_prepared), &available_dimensions);
|
|
|
|
|
|
|
|
success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
|
|
|
|
if (!success)
|
|
|
|
goto out;
|
|
|
|
success = gdk_pixbuf_loader_close (pixbuf_loader, error);
|
|
|
|
if (!success)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
|
|
|
|
|
|
|
|
width_before_rotation = gdk_pixbuf_get_width (pixbuf);
|
|
|
|
|
|
|
|
rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
|
|
|
|
width_after_rotation = gdk_pixbuf_get_width (rotated_pixbuf);
|
|
|
|
|
2011-10-18 21:21:10 -04:00
|
|
|
/* There is currently no way to tell if the pixbuf will need to be rotated before it is loaded,
|
|
|
|
* so we only check that once it is loaded, and reload it again if it needs to be rotated in order
|
|
|
|
* to use the available width and height correctly.
|
|
|
|
* See http://bugzilla.gnome.org/show_bug.cgi?id=579003
|
|
|
|
*/
|
2010-02-09 12:42:07 -05:00
|
|
|
if (width_before_rotation != width_after_rotation)
|
|
|
|
{
|
|
|
|
g_object_unref (pixbuf_loader);
|
|
|
|
g_object_unref (rotated_pixbuf);
|
|
|
|
rotated_pixbuf = NULL;
|
|
|
|
|
|
|
|
pixbuf_loader = gdk_pixbuf_loader_new ();
|
|
|
|
|
2011-10-18 21:21:10 -04:00
|
|
|
/* We know that the image will later be rotated, so we reverse the available dimensions. */
|
2010-02-09 12:42:07 -05:00
|
|
|
available_dimensions.width = available_height;
|
|
|
|
available_dimensions.height = available_width;
|
2014-03-22 23:29:23 -04:00
|
|
|
available_dimensions.scale = scale;
|
2010-02-09 12:42:07 -05:00
|
|
|
g_signal_connect (pixbuf_loader, "size-prepared",
|
|
|
|
G_CALLBACK (on_image_size_prepared), &available_dimensions);
|
|
|
|
|
|
|
|
success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
|
|
|
|
if (!success)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
success = gdk_pixbuf_loader_close (pixbuf_loader, error);
|
|
|
|
if (!success)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
|
|
|
|
|
|
|
|
rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (pixbuf_loader)
|
|
|
|
g_object_unref (pixbuf_loader);
|
|
|
|
return rotated_pixbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GdkPixbuf *
|
2014-09-18 20:04:00 -04:00
|
|
|
impl_load_pixbuf_file (GFile *file,
|
2010-02-09 12:42:07 -05:00
|
|
|
int available_width,
|
|
|
|
int available_height,
|
2017-05-11 00:25:00 -04:00
|
|
|
int paint_scale,
|
|
|
|
float resource_scale,
|
2010-02-09 12:42:07 -05:00
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
GdkPixbuf *pixbuf = NULL;
|
|
|
|
char *contents = NULL;
|
|
|
|
gsize size;
|
|
|
|
|
|
|
|
if (g_file_load_contents (file, NULL, &contents, &size, NULL, error))
|
|
|
|
{
|
2017-05-11 00:25:00 -04:00
|
|
|
int scale = ceilf (paint_scale * resource_scale);
|
2010-02-09 12:42:07 -05:00
|
|
|
pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size,
|
|
|
|
available_width, available_height,
|
2014-03-22 23:29:23 -04:00
|
|
|
scale,
|
2010-02-09 12:42:07 -05:00
|
|
|
error);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (contents);
|
|
|
|
|
|
|
|
return pixbuf;
|
|
|
|
}
|
|
|
|
|
2009-09-08 15:47:30 -04:00
|
|
|
static void
|
2015-09-25 14:25:41 -04:00
|
|
|
load_pixbuf_thread (GTask *result,
|
|
|
|
gpointer source,
|
|
|
|
gpointer task_data,
|
2010-02-09 12:42:07 -05:00
|
|
|
GCancellable *cancellable)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2010-02-09 12:42:07 -05:00
|
|
|
GdkPixbuf *pixbuf;
|
2015-09-25 14:25:41 -04:00
|
|
|
AsyncTextureLoadData *data = task_data;
|
2010-02-09 12:42:07 -05:00
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
g_assert (data != NULL);
|
2014-09-18 20:04:00 -04:00
|
|
|
g_assert (data->file != NULL);
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2017-05-11 00:25:00 -04:00
|
|
|
pixbuf = impl_load_pixbuf_file (data->file, data->width, data->height,
|
|
|
|
data->paint_scale, data->resource_scale,
|
|
|
|
&error);
|
2010-02-09 12:42:07 -05:00
|
|
|
|
|
|
|
if (error != NULL)
|
2015-09-25 14:25:41 -04:00
|
|
|
g_task_return_error (result, error);
|
|
|
|
else if (pixbuf)
|
|
|
|
g_task_return_pointer (result, g_object_ref (pixbuf), g_object_unref);
|
2017-04-07 05:56:01 -04:00
|
|
|
|
|
|
|
g_clear_object (&pixbuf);
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
static GdkPixbuf *
|
|
|
|
load_pixbuf_async_finish (StTextureCache *cache, GAsyncResult *result, GError **error)
|
|
|
|
{
|
2015-09-25 14:25:41 -04:00
|
|
|
return g_task_propagate_pointer (G_TASK (result), error);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
static ClutterContent *
|
2019-02-26 21:18:39 -05:00
|
|
|
pixbuf_to_st_content_image (GdkPixbuf *pixbuf,
|
|
|
|
int width,
|
|
|
|
int height,
|
|
|
|
int paint_scale,
|
|
|
|
float resource_scale)
|
2012-09-06 05:40:10 -04:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
ClutterContent *image;
|
|
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
|
texture-cache: Keep aspect ratio for content images
Images are loaded either with a supplied fixed size, or using the "native"
dimensions of the file. When creating a content image from the loaded data,
we currently simply apply this directly to the preferred size.
This works usually fine: GdkPixbuf will always keep the aspect ratio, so
if only one dimension is provided, the other will be adjusted accordingly:
Loading a 200x200 image with a requested size of (100, -1) will result in
a 100x100 content image.
There is a catch though: GdkPixbuf will only scale *down* to the requested
size, no up. That is, loading a 100x100 image with a requested size of
(200, -1) will result in a 100x100 pixbuf. But as we assume that the pixbuf
size matches the requested size, the image content ends up with 200x100.
Fix this by explicitly handling the case where only one size was supplied,
and make the other dimension take the aspect ratio into account
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/525
2019-04-30 14:37:34 -04:00
|
|
|
float native_width, native_height;
|
|
|
|
|
|
|
|
native_width = ceilf (gdk_pixbuf_get_width (pixbuf) / resource_scale);
|
|
|
|
native_height = ceilf (gdk_pixbuf_get_height (pixbuf) / resource_scale);
|
2019-02-26 21:18:39 -05:00
|
|
|
|
texture-cache: Keep aspect ratio for content images
Images are loaded either with a supplied fixed size, or using the "native"
dimensions of the file. When creating a content image from the loaded data,
we currently simply apply this directly to the preferred size.
This works usually fine: GdkPixbuf will always keep the aspect ratio, so
if only one dimension is provided, the other will be adjusted accordingly:
Loading a 200x200 image with a requested size of (100, -1) will result in
a 100x100 content image.
There is a catch though: GdkPixbuf will only scale *down* to the requested
size, no up. That is, loading a 100x100 image with a requested size of
(200, -1) will result in a 100x100 pixbuf. But as we assume that the pixbuf
size matches the requested size, the image content ends up with 200x100.
Fix this by explicitly handling the case where only one size was supplied,
and make the other dimension take the aspect ratio into account
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/525
2019-04-30 14:37:34 -04:00
|
|
|
if (width < 0 && height < 0)
|
|
|
|
{
|
|
|
|
width = native_width;
|
|
|
|
height = native_height;
|
|
|
|
}
|
|
|
|
else if (width < 0)
|
|
|
|
{
|
|
|
|
height *= paint_scale;
|
|
|
|
width = native_width * (height / native_height);
|
|
|
|
}
|
|
|
|
else if (height < 0)
|
|
|
|
{
|
|
|
|
width *= paint_scale;
|
|
|
|
height = native_height * (width / native_width);
|
|
|
|
}
|
2019-02-26 21:18:39 -05:00
|
|
|
else
|
texture-cache: Keep aspect ratio for content images
Images are loaded either with a supplied fixed size, or using the "native"
dimensions of the file. When creating a content image from the loaded data,
we currently simply apply this directly to the preferred size.
This works usually fine: GdkPixbuf will always keep the aspect ratio, so
if only one dimension is provided, the other will be adjusted accordingly:
Loading a 200x200 image with a requested size of (100, -1) will result in
a 100x100 content image.
There is a catch though: GdkPixbuf will only scale *down* to the requested
size, no up. That is, loading a 100x100 image with a requested size of
(200, -1) will result in a 100x100 pixbuf. But as we assume that the pixbuf
size matches the requested size, the image content ends up with 200x100.
Fix this by explicitly handling the case where only one size was supplied,
and make the other dimension take the aspect ratio into account
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/525
2019-04-30 14:37:34 -04:00
|
|
|
{
|
|
|
|
width *= paint_scale;
|
|
|
|
height *= paint_scale;
|
|
|
|
}
|
2019-02-26 21:18:39 -05:00
|
|
|
|
|
|
|
image = st_image_content_new_with_preferred_size (width, height);
|
2019-01-24 10:42:14 -05:00
|
|
|
clutter_image_set_data (CLUTTER_IMAGE (image),
|
|
|
|
gdk_pixbuf_get_pixels (pixbuf),
|
|
|
|
gdk_pixbuf_get_has_alpha (pixbuf) ?
|
|
|
|
COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
|
|
|
|
gdk_pixbuf_get_width (pixbuf),
|
|
|
|
gdk_pixbuf_get_height (pixbuf),
|
|
|
|
gdk_pixbuf_get_rowstride (pixbuf),
|
|
|
|
&error);
|
2016-04-14 11:13:17 -04:00
|
|
|
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
g_warning ("Failed to allocate texture: %s", error->message);
|
2019-01-24 10:42:14 -05:00
|
|
|
g_clear_object (&image);
|
2016-04-14 11:13:17 -04:00
|
|
|
}
|
2014-08-07 14:40:01 -04:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
return image;
|
2012-09-06 05:40:10 -04:00
|
|
|
}
|
|
|
|
|
2010-12-10 17:15:39 -05:00
|
|
|
static cairo_surface_t *
|
|
|
|
pixbuf_to_cairo_surface (GdkPixbuf *pixbuf)
|
|
|
|
{
|
|
|
|
cairo_surface_t *dummy_surface;
|
|
|
|
cairo_pattern_t *pattern;
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
cairo_t *cr;
|
|
|
|
|
|
|
|
dummy_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
|
|
|
|
|
|
|
|
cr = cairo_create (dummy_surface);
|
|
|
|
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
|
|
|
|
pattern = cairo_get_source (cr);
|
|
|
|
cairo_pattern_get_surface (pattern, &surface);
|
|
|
|
cairo_surface_reference (surface);
|
|
|
|
cairo_destroy (cr);
|
|
|
|
cairo_surface_destroy (dummy_surface);
|
|
|
|
|
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
static void
|
2013-02-14 15:30:46 -05:00
|
|
|
finish_texture_load (AsyncTextureLoadData *data,
|
|
|
|
GdkPixbuf *pixbuf)
|
2010-02-09 12:42:07 -05:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
g_autoptr(ClutterContent) image = NULL;
|
2010-02-09 12:42:07 -05:00
|
|
|
GSList *iter;
|
|
|
|
StTextureCache *cache;
|
|
|
|
|
2013-02-14 15:30:46 -05:00
|
|
|
cache = data->cache;
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2010-02-09 15:12:28 -05:00
|
|
|
g_hash_table_remove (cache->priv->outstanding_requests, data->key);
|
2010-02-09 12:42:07 -05:00
|
|
|
|
|
|
|
if (pixbuf == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (data->policy != ST_TEXTURE_CACHE_POLICY_NONE)
|
|
|
|
{
|
2019-01-24 10:57:40 -05:00
|
|
|
gpointer orig_key = NULL, value = NULL;
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2010-02-09 15:12:28 -05:00
|
|
|
if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key,
|
2010-02-09 12:42:07 -05:00
|
|
|
&orig_key, &value))
|
|
|
|
{
|
2019-02-26 21:18:39 -05:00
|
|
|
image = pixbuf_to_st_content_image (pixbuf,
|
|
|
|
data->width, data->height,
|
|
|
|
data->paint_scale,
|
|
|
|
data->resource_scale);
|
2019-01-24 10:57:40 -05:00
|
|
|
if (!image)
|
|
|
|
goto out;
|
|
|
|
|
2010-02-09 15:12:28 -05:00
|
|
|
g_hash_table_insert (cache->priv->keyed_cache, g_strdup (data->key),
|
2019-01-24 10:42:14 -05:00
|
|
|
g_object_ref (image));
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
2019-01-24 10:57:40 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
image = g_object_ref (value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-02-26 21:18:39 -05:00
|
|
|
image = pixbuf_to_st_content_image (pixbuf,
|
|
|
|
data->width, data->height,
|
|
|
|
data->paint_scale,
|
|
|
|
data->resource_scale);
|
2019-01-24 10:57:40 -05:00
|
|
|
if (!image)
|
|
|
|
goto out;
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
for (iter = data->actors; iter; iter = iter->next)
|
2010-02-09 12:42:07 -05:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
ClutterActor *actor = iter->data;
|
|
|
|
set_content_from_image (actor, image);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2013-02-14 16:04:03 -05:00
|
|
|
texture_load_data_free (data);
|
2013-02-14 15:30:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_symbolic_icon_loaded (GObject *source,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
pixbuf = gtk_icon_info_load_symbolic_finish (GTK_ICON_INFO (source), result, NULL, NULL);
|
|
|
|
finish_texture_load (user_data, pixbuf);
|
|
|
|
g_clear_object (&pixbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_icon_loaded (GObject *source,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
pixbuf = gtk_icon_info_load_icon_finish (GTK_ICON_INFO (source), result, NULL);
|
|
|
|
finish_texture_load (user_data, pixbuf);
|
|
|
|
g_clear_object (&pixbuf);
|
|
|
|
}
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2013-02-14 15:30:46 -05:00
|
|
|
static void
|
|
|
|
on_pixbuf_loaded (GObject *source,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
pixbuf = load_pixbuf_async_finish (ST_TEXTURE_CACHE (source), result, NULL);
|
|
|
|
finish_texture_load (user_data, pixbuf);
|
|
|
|
g_clear_object (&pixbuf);
|
2011-10-05 05:00:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
load_texture_async (StTextureCache *cache,
|
|
|
|
AsyncTextureLoadData *data)
|
|
|
|
{
|
2014-09-18 20:04:00 -04:00
|
|
|
if (data->file)
|
2013-02-14 15:30:46 -05:00
|
|
|
{
|
2015-09-25 14:25:41 -04:00
|
|
|
GTask *task = g_task_new (cache, NULL, on_pixbuf_loaded, data);
|
2015-10-31 19:21:10 -04:00
|
|
|
g_task_set_task_data (task, data, NULL);
|
2015-09-25 14:25:41 -04:00
|
|
|
g_task_run_in_thread (task, load_pixbuf_thread);
|
|
|
|
g_object_unref (task);
|
2013-02-14 15:30:46 -05:00
|
|
|
}
|
|
|
|
else if (data->icon_info)
|
|
|
|
{
|
|
|
|
StIconColors *colors = data->colors;
|
|
|
|
if (colors)
|
|
|
|
{
|
|
|
|
GdkRGBA foreground_color;
|
|
|
|
GdkRGBA success_color;
|
|
|
|
GdkRGBA warning_color;
|
|
|
|
GdkRGBA error_color;
|
|
|
|
|
|
|
|
rgba_from_clutter (&foreground_color, &colors->foreground);
|
|
|
|
rgba_from_clutter (&success_color, &colors->success);
|
|
|
|
rgba_from_clutter (&warning_color, &colors->warning);
|
|
|
|
rgba_from_clutter (&error_color, &colors->error);
|
|
|
|
|
|
|
|
gtk_icon_info_load_symbolic_async (data->icon_info,
|
|
|
|
&foreground_color, &success_color,
|
|
|
|
&warning_color, &error_color,
|
2020-05-22 16:53:39 -04:00
|
|
|
cache->priv->cancellable,
|
|
|
|
on_symbolic_icon_loaded, data);
|
2013-02-14 15:30:46 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-05-22 16:53:39 -04:00
|
|
|
gtk_icon_info_load_icon_async (data->icon_info,
|
|
|
|
cache->priv->cancellable,
|
|
|
|
on_icon_loaded, data);
|
2013-02-14 15:30:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
g_assert_not_reached ();
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
typedef struct {
|
|
|
|
StTextureCache *cache;
|
2020-06-30 10:48:16 -04:00
|
|
|
ClutterContent *image;
|
2010-02-09 12:42:07 -05:00
|
|
|
GObject *source;
|
2019-11-21 17:00:53 -05:00
|
|
|
gulong notify_signal_id;
|
2010-02-09 12:42:07 -05:00
|
|
|
gboolean weakref_active;
|
|
|
|
} StTextureCachePropertyBind;
|
|
|
|
|
2009-09-08 15:47:30 -04:00
|
|
|
static void
|
2010-02-09 12:42:07 -05:00
|
|
|
st_texture_cache_reset_texture (StTextureCachePropertyBind *bind,
|
|
|
|
const char *propname)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2015-01-12 15:21:39 -05:00
|
|
|
cairo_surface_t *surface;
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2015-01-12 15:21:39 -05:00
|
|
|
g_object_get (bind->source, propname, &surface, NULL);
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2015-01-12 15:21:39 -05:00
|
|
|
if (surface != NULL &&
|
|
|
|
cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE &&
|
|
|
|
(cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ||
|
|
|
|
cairo_image_surface_get_format (surface) == CAIRO_FORMAT_RGB24))
|
2010-02-09 12:42:07 -05:00
|
|
|
{
|
2019-01-24 13:02:30 -05:00
|
|
|
g_autoptr(GError) error = NULL;
|
2020-06-30 10:48:16 -04:00
|
|
|
int width, height, size;
|
2019-03-08 08:08:27 -05:00
|
|
|
|
2020-06-30 10:48:16 -04:00
|
|
|
width = cairo_image_surface_get_width (surface);
|
|
|
|
height = cairo_image_surface_get_width (surface);
|
|
|
|
size = MAX(width, height);
|
2019-01-24 10:42:14 -05:00
|
|
|
|
2020-06-30 10:48:16 -04:00
|
|
|
if (!bind->image)
|
|
|
|
bind->image = st_image_content_new_with_preferred_size (size, size);
|
2019-01-24 10:42:14 -05:00
|
|
|
|
2020-06-30 10:48:16 -04:00
|
|
|
clutter_image_set_data (CLUTTER_IMAGE (bind->image),
|
2019-01-24 10:42:14 -05:00
|
|
|
cairo_image_surface_get_data (surface),
|
|
|
|
cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ?
|
|
|
|
COGL_PIXEL_FORMAT_BGRA_8888 : COGL_PIXEL_FORMAT_BGR_888,
|
2020-06-30 10:48:16 -04:00
|
|
|
width,
|
|
|
|
height,
|
2019-01-24 10:42:14 -05:00
|
|
|
cairo_image_surface_get_stride (surface),
|
|
|
|
&error);
|
|
|
|
|
2020-06-30 10:48:16 -04:00
|
|
|
if (error)
|
2019-01-24 13:02:30 -05:00
|
|
|
g_warning ("Failed to allocate texture: %s", error->message);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
else
|
2020-06-30 10:48:16 -04:00
|
|
|
bind->image = g_object_new (ST_TYPE_IMAGE_CONTENT,
|
|
|
|
"preferred-width", 0, /* tough luck */
|
|
|
|
"preferred-height", 0,
|
|
|
|
NULL);
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
static void
|
|
|
|
st_texture_cache_on_pixbuf_notify (GObject *object,
|
|
|
|
GParamSpec *paramspec,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
StTextureCachePropertyBind *bind = data;
|
|
|
|
st_texture_cache_reset_texture (bind, paramspec->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
st_texture_cache_bind_weak_notify (gpointer data,
|
|
|
|
GObject *source_location)
|
|
|
|
{
|
|
|
|
StTextureCachePropertyBind *bind = data;
|
|
|
|
bind->weakref_active = FALSE;
|
2021-02-12 17:10:25 -05:00
|
|
|
if (G_OBJECT (bind->image) != source_location)
|
|
|
|
g_object_weak_unref (G_OBJECT (bind->image), st_texture_cache_bind_weak_notify, bind);
|
|
|
|
if (bind->source != source_location)
|
|
|
|
g_object_weak_unref (G_OBJECT (bind->source), st_texture_cache_bind_weak_notify, bind);
|
2020-03-08 21:17:19 -04:00
|
|
|
g_signal_handler_disconnect (bind->source, bind->notify_signal_id);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
st_texture_cache_free_bind (gpointer data)
|
|
|
|
{
|
|
|
|
StTextureCachePropertyBind *bind = data;
|
2021-02-12 17:10:25 -05:00
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
if (bind->weakref_active)
|
2021-02-12 17:10:25 -05:00
|
|
|
{
|
|
|
|
g_object_weak_unref (G_OBJECT (bind->image), st_texture_cache_bind_weak_notify, bind);
|
|
|
|
g_object_weak_unref (G_OBJECT (bind->source), st_texture_cache_bind_weak_notify, bind);
|
|
|
|
}
|
|
|
|
|
2020-10-19 14:12:27 -04:00
|
|
|
g_free (bind);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
2009-09-08 15:47:30 -04:00
|
|
|
|
|
|
|
/**
|
2015-01-12 15:21:39 -05:00
|
|
|
* st_texture_cache_bind_cairo_surface_property:
|
2020-05-20 19:50:09 -04:00
|
|
|
* @cache: A #StTextureCache
|
2020-06-30 10:48:16 -04:00
|
|
|
* @object: A #GObject with a property @property_name of type #cairo_surface_t
|
2010-02-09 12:42:07 -05:00
|
|
|
* @property_name: Name of a property
|
|
|
|
*
|
2020-06-30 10:48:16 -04:00
|
|
|
* Create a #GIcon which tracks the #cairo_surface_t value of a GObject property
|
2010-02-09 12:42:07 -05:00
|
|
|
* named by @property_name. Unlike other methods in StTextureCache, the underlying
|
2014-08-07 14:40:01 -04:00
|
|
|
* #CoglTexture is not shared by default with other invocations to this method.
|
2009-09-08 15:47:30 -04:00
|
|
|
*
|
2010-02-09 12:42:07 -05:00
|
|
|
* If the source object is destroyed, the texture will continue to show the last
|
|
|
|
* value of the property.
|
2009-09-08 15:47:30 -04:00
|
|
|
*
|
2020-05-20 19:50:09 -04:00
|
|
|
* Returns: (transfer none): A new #GIcon
|
2009-09-08 15:47:30 -04:00
|
|
|
*/
|
2020-06-30 10:48:16 -04:00
|
|
|
GIcon *
|
2015-01-12 15:21:39 -05:00
|
|
|
st_texture_cache_bind_cairo_surface_property (StTextureCache *cache,
|
|
|
|
GObject *object,
|
2020-06-30 10:48:16 -04:00
|
|
|
const char *property_name)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2010-02-09 12:42:07 -05:00
|
|
|
gchar *notify_key;
|
|
|
|
StTextureCachePropertyBind *bind;
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2020-10-19 14:12:27 -04:00
|
|
|
bind = g_new0 (StTextureCachePropertyBind, 1);
|
2010-02-09 12:42:07 -05:00
|
|
|
bind->cache = cache;
|
|
|
|
bind->source = object;
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
st_texture_cache_reset_texture (bind, property_name);
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2020-06-30 10:48:16 -04:00
|
|
|
g_object_weak_ref (G_OBJECT (bind->image), st_texture_cache_bind_weak_notify, bind);
|
2021-02-12 17:10:25 -05:00
|
|
|
g_object_weak_ref (G_OBJECT (bind->source), st_texture_cache_bind_weak_notify, bind);
|
2020-06-30 10:48:16 -04:00
|
|
|
bind->weakref_active = TRUE;
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
notify_key = g_strdup_printf ("notify::%s", property_name);
|
|
|
|
bind->notify_signal_id = g_signal_connect_data (object, notify_key, G_CALLBACK(st_texture_cache_on_pixbuf_notify),
|
|
|
|
bind, (GClosureNotify)st_texture_cache_free_bind, 0);
|
|
|
|
g_free (notify_key);
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2020-06-30 10:48:16 -04:00
|
|
|
return G_ICON (bind->image);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
|
2010-02-09 16:53:59 -05:00
|
|
|
/**
|
2010-09-01 20:48:11 -04:00
|
|
|
* st_texture_cache_load: (skip)
|
2010-02-09 16:53:59 -05:00
|
|
|
* @cache: A #StTextureCache
|
|
|
|
* @key: Arbitrary string used to refer to item
|
|
|
|
* @policy: Caching policy
|
|
|
|
* @load: Function to create the texture, if not already cached
|
|
|
|
* @data: User data passed to @load
|
|
|
|
* @error: A #GError
|
|
|
|
*
|
|
|
|
* Load an arbitrary texture, caching it. The string chosen for @key
|
|
|
|
* should be of the form "type-prefix:type-uuid". For example,
|
|
|
|
* "url:file:///usr/share/icons/hicolor/48x48/apps/firefox.png", or
|
|
|
|
* "stock-icon:gtk-ok".
|
|
|
|
*
|
|
|
|
* Returns: (transfer full): A newly-referenced handle to the texture
|
|
|
|
*/
|
2014-08-07 14:40:01 -04:00
|
|
|
CoglTexture *
|
2010-02-09 16:53:59 -05:00
|
|
|
st_texture_cache_load (StTextureCache *cache,
|
|
|
|
const char *key,
|
|
|
|
StTextureCachePolicy policy,
|
|
|
|
StTextureCacheLoader load,
|
|
|
|
void *data,
|
|
|
|
GError **error)
|
|
|
|
{
|
2014-08-07 14:40:01 -04:00
|
|
|
CoglTexture *texture;
|
2010-02-09 16:53:59 -05:00
|
|
|
|
|
|
|
texture = g_hash_table_lookup (cache->priv->keyed_cache, key);
|
|
|
|
if (!texture)
|
|
|
|
{
|
|
|
|
texture = load (cache, key, data, error);
|
2011-11-04 16:03:33 -04:00
|
|
|
if (texture && policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
|
2010-02-09 16:53:59 -05:00
|
|
|
g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texture);
|
|
|
|
}
|
2014-08-07 14:40:01 -04:00
|
|
|
|
2011-11-04 16:03:33 -04:00
|
|
|
if (texture && policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
|
|
|
|
cogl_object_ref (texture);
|
|
|
|
|
2010-02-09 16:53:59 -05:00
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
/**
|
2012-03-16 23:48:22 -04:00
|
|
|
* ensure_request:
|
2020-05-20 19:50:09 -04:00
|
|
|
* @cache: A #StTextureCache
|
2010-02-09 15:12:28 -05:00
|
|
|
* @key: A cache key
|
2011-10-04 19:18:16 -04:00
|
|
|
* @policy: Cache policy
|
2010-02-09 12:42:07 -05:00
|
|
|
* @request: (out): If no request is outstanding, one will be created and returned here
|
2012-03-16 23:48:22 -04:00
|
|
|
* @texture: A texture to be added to the request
|
2010-02-09 12:42:07 -05:00
|
|
|
*
|
|
|
|
* Check for any outstanding load for the data represented by @key. If there
|
|
|
|
* is already a request pending, append it to that request to avoid loading
|
|
|
|
* the data multiple times.
|
|
|
|
*
|
2019-01-24 10:42:14 -05:00
|
|
|
* Returns: %TRUE if there is already a request pending
|
2010-02-09 12:42:07 -05:00
|
|
|
*/
|
|
|
|
static gboolean
|
2012-03-16 23:48:22 -04:00
|
|
|
ensure_request (StTextureCache *cache,
|
|
|
|
const char *key,
|
|
|
|
StTextureCachePolicy policy,
|
|
|
|
AsyncTextureLoadData **request,
|
2019-01-24 10:42:14 -05:00
|
|
|
ClutterActor *actor)
|
2010-02-09 12:42:07 -05:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
ClutterContent *image;
|
2010-02-09 12:42:07 -05:00
|
|
|
AsyncTextureLoadData *pending;
|
|
|
|
gboolean had_pending;
|
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
image = g_hash_table_lookup (cache->priv->keyed_cache, key);
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
if (image != NULL)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2010-02-09 12:42:07 -05:00
|
|
|
/* We had this cached already, just set the texture and we're done. */
|
2019-01-24 10:42:14 -05:00
|
|
|
set_content_from_image (actor, image);
|
2010-02-09 12:42:07 -05:00
|
|
|
return TRUE;
|
2009-09-08 15:47:30 -04:00
|
|
|
}
|
2010-02-09 12:42:07 -05:00
|
|
|
|
|
|
|
pending = g_hash_table_lookup (cache->priv->outstanding_requests, key);
|
|
|
|
had_pending = pending != NULL;
|
|
|
|
|
|
|
|
if (pending == NULL)
|
2009-09-08 15:47:30 -04:00
|
|
|
{
|
2010-02-09 12:42:07 -05:00
|
|
|
/* Not cached and no pending request, create it */
|
2020-10-19 14:12:27 -04:00
|
|
|
*request = g_new0 (AsyncTextureLoadData, 1);
|
2011-10-04 19:18:16 -04:00
|
|
|
if (policy != ST_TEXTURE_CACHE_POLICY_NONE)
|
|
|
|
g_hash_table_insert (cache->priv->outstanding_requests, g_strdup (key), *request);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
*request = pending;
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
/* Regardless of whether there was a pending request, prepend our texture here. */
|
2019-01-24 10:42:14 -05:00
|
|
|
(*request)->actors = g_slist_prepend ((*request)->actors, g_object_ref (actor));
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
return had_pending;
|
|
|
|
}
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2014-11-29 09:43:50 -05:00
|
|
|
/**
|
|
|
|
* st_texture_cache_load_gicon:
|
2020-05-20 19:50:09 -04:00
|
|
|
* @cache: A #StTextureCache
|
|
|
|
* @theme_node: (nullable): The #StThemeNode to use for colors, or %NULL
|
2014-11-29 09:43:50 -05:00
|
|
|
* if the icon must not be recolored
|
|
|
|
* @icon: the #GIcon to load
|
|
|
|
* @size: Size of themed
|
2017-05-11 00:25:00 -04:00
|
|
|
* @paint_scale: Scale factor of display
|
|
|
|
* @resource_scale: Resource scale factor
|
2014-11-29 09:43:50 -05:00
|
|
|
*
|
|
|
|
* This method returns a new #ClutterActor for a given #GIcon. If the
|
|
|
|
* icon isn't loaded already, the texture will be filled
|
|
|
|
* asynchronously.
|
|
|
|
*
|
2020-05-20 19:50:09 -04:00
|
|
|
* Returns: (transfer none) (nullable): A new #ClutterActor for the icon, or %NULL if not found
|
2014-11-29 09:43:50 -05:00
|
|
|
*/
|
|
|
|
ClutterActor *
|
|
|
|
st_texture_cache_load_gicon (StTextureCache *cache,
|
|
|
|
StThemeNode *theme_node,
|
|
|
|
GIcon *icon,
|
|
|
|
gint size,
|
2017-05-11 00:25:00 -04:00
|
|
|
gint paint_scale,
|
|
|
|
gfloat resource_scale)
|
2010-02-09 12:42:07 -05:00
|
|
|
{
|
|
|
|
AsyncTextureLoadData *request;
|
2019-01-24 10:42:14 -05:00
|
|
|
ClutterActor *actor;
|
2017-05-11 00:25:00 -04:00
|
|
|
gint scale;
|
2010-02-09 15:12:28 -05:00
|
|
|
char *gicon_string;
|
2021-01-05 08:40:15 -05:00
|
|
|
g_autofree char *key = NULL;
|
2017-05-11 00:25:00 -04:00
|
|
|
float actor_size;
|
2010-02-09 12:42:07 -05:00
|
|
|
GtkIconTheme *theme;
|
2011-09-30 13:57:09 -04:00
|
|
|
StTextureCachePolicy policy;
|
2014-11-29 09:43:50 -05:00
|
|
|
StIconColors *colors = NULL;
|
2014-11-29 09:53:36 -05:00
|
|
|
StIconStyle icon_style = ST_ICON_STYLE_REQUESTED;
|
2014-09-05 12:56:21 -04:00
|
|
|
GtkIconLookupFlags lookup_flags;
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2020-06-29 22:12:16 -04:00
|
|
|
actor_size = size * paint_scale;
|
|
|
|
|
|
|
|
if (ST_IS_IMAGE_CONTENT (icon))
|
|
|
|
{
|
|
|
|
return g_object_new (CLUTTER_TYPE_ACTOR,
|
|
|
|
"request-mode", CLUTTER_REQUEST_CONTENT_SIZE,
|
|
|
|
"width", actor_size,
|
|
|
|
"height", actor_size,
|
|
|
|
"content", CLUTTER_CONTENT (icon),
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2014-11-29 09:43:50 -05:00
|
|
|
if (theme_node)
|
2014-11-29 09:53:36 -05:00
|
|
|
{
|
|
|
|
colors = st_theme_node_get_icon_colors (theme_node);
|
|
|
|
icon_style = st_theme_node_get_icon_style (theme_node);
|
|
|
|
}
|
2014-11-29 09:43:50 -05:00
|
|
|
|
2011-10-18 21:52:46 -04:00
|
|
|
/* Do theme lookups in the main thread to avoid thread-unsafety */
|
|
|
|
theme = cache->priv->icon_theme;
|
|
|
|
|
2014-09-05 12:56:21 -04:00
|
|
|
lookup_flags = GTK_ICON_LOOKUP_USE_BUILTIN;
|
|
|
|
|
2014-11-29 09:53:36 -05:00
|
|
|
if (icon_style == ST_ICON_STYLE_REGULAR)
|
|
|
|
lookup_flags |= GTK_ICON_LOOKUP_FORCE_REGULAR;
|
|
|
|
else if (icon_style == ST_ICON_STYLE_SYMBOLIC)
|
|
|
|
lookup_flags |= GTK_ICON_LOOKUP_FORCE_SYMBOLIC;
|
|
|
|
|
2014-09-05 12:56:21 -04:00
|
|
|
if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL)
|
|
|
|
lookup_flags |= GTK_ICON_LOOKUP_DIR_RTL;
|
|
|
|
else
|
|
|
|
lookup_flags |= GTK_ICON_LOOKUP_DIR_LTR;
|
|
|
|
|
2017-05-11 00:25:00 -04:00
|
|
|
scale = ceilf (paint_scale * resource_scale);
|
2011-10-18 21:52:46 -04:00
|
|
|
|
2010-02-09 15:12:28 -05:00
|
|
|
gicon_string = g_icon_to_string (icon);
|
2011-09-30 13:57:09 -04:00
|
|
|
/* A return value of NULL indicates that the icon can not be serialized,
|
|
|
|
* so don't have a unique identifier for it as a cache key, and thus can't
|
2020-08-19 05:26:11 -04:00
|
|
|
* be cached. If it is cacheable, we hardcode a policy of FOREVER here for
|
2011-09-30 13:57:09 -04:00
|
|
|
* now; we should actually blow this away on icon theme changes probably */
|
|
|
|
policy = gicon_string != NULL ? ST_TEXTURE_CACHE_POLICY_FOREVER
|
|
|
|
: ST_TEXTURE_CACHE_POLICY_NONE;
|
2010-11-02 16:12:56 -04:00
|
|
|
if (colors)
|
|
|
|
{
|
|
|
|
/* This raises some doubts about the practice of using string keys */
|
2014-11-29 09:53:36 -05:00
|
|
|
key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,scale=%d,style=%d,colors=%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x",
|
|
|
|
gicon_string, size, scale, icon_style,
|
2010-11-02 16:12:56 -04:00
|
|
|
colors->foreground.red, colors->foreground.blue, colors->foreground.green, colors->foreground.alpha,
|
|
|
|
colors->warning.red, colors->warning.blue, colors->warning.green, colors->warning.alpha,
|
|
|
|
colors->error.red, colors->error.blue, colors->error.green, colors->error.alpha,
|
|
|
|
colors->success.red, colors->success.blue, colors->success.green, colors->success.alpha);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-11-29 09:53:36 -05:00
|
|
|
key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,scale=%d,style=%d",
|
|
|
|
gicon_string, size, scale, icon_style);
|
2010-11-02 16:12:56 -04:00
|
|
|
}
|
2010-02-09 15:12:28 -05:00
|
|
|
g_free (gicon_string);
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
actor = create_invisible_actor ();
|
2017-05-11 00:25:00 -04:00
|
|
|
clutter_actor_set_size (actor, actor_size, actor_size);
|
2021-01-05 08:45:55 -05:00
|
|
|
if (!ensure_request (cache, key, policy, &request, actor))
|
2010-02-09 12:42:07 -05:00
|
|
|
{
|
2011-10-18 21:52:46 -04:00
|
|
|
/* Else, make a new request */
|
2021-01-05 08:45:55 -05:00
|
|
|
GtkIconInfo *info;
|
|
|
|
|
|
|
|
info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon,
|
|
|
|
size, scale,
|
|
|
|
lookup_flags);
|
|
|
|
if (info == NULL)
|
|
|
|
{
|
|
|
|
g_hash_table_remove (cache->priv->outstanding_requests, key);
|
|
|
|
texture_load_data_free (request);
|
|
|
|
g_object_unref (actor);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-10-18 21:52:46 -04:00
|
|
|
|
2011-10-05 05:00:23 -04:00
|
|
|
request->cache = cache;
|
2011-10-18 21:52:46 -04:00
|
|
|
/* Transfer ownership of key */
|
2021-01-05 08:40:15 -05:00
|
|
|
request->key = g_steal_pointer (&key);
|
2011-09-30 13:57:09 -04:00
|
|
|
request->policy = policy;
|
2011-10-29 10:41:51 -04:00
|
|
|
request->colors = colors ? st_icon_colors_ref (colors) : NULL;
|
2010-02-09 12:42:07 -05:00
|
|
|
request->icon_info = info;
|
2014-03-23 00:05:53 -04:00
|
|
|
request->width = request->height = size;
|
2017-05-11 00:25:00 -04:00
|
|
|
request->paint_scale = paint_scale;
|
|
|
|
request->resource_scale = resource_scale;
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2011-10-05 05:00:23 -04:00
|
|
|
load_texture_async (cache, request);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
return actor;
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
|
|
|
|
2010-06-08 06:15:16 -04:00
|
|
|
static ClutterActor *
|
2019-02-25 15:26:46 -05:00
|
|
|
load_from_pixbuf (GdkPixbuf *pixbuf,
|
|
|
|
int paint_scale,
|
|
|
|
float resource_scale)
|
2010-06-08 06:15:16 -04:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
g_autoptr(ClutterContent) image = NULL;
|
|
|
|
ClutterActor *actor;
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2019-02-25 15:26:46 -05:00
|
|
|
image = pixbuf_to_st_content_image (pixbuf, -1, -1, paint_scale, resource_scale);
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2019-02-26 21:31:19 -05:00
|
|
|
actor = g_object_new (CLUTTER_TYPE_ACTOR,
|
|
|
|
"request-mode", CLUTTER_REQUEST_CONTENT_SIZE,
|
|
|
|
NULL);
|
2019-01-24 10:42:14 -05:00
|
|
|
clutter_actor_set_content (actor, image);
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
return actor;
|
2010-06-08 06:15:16 -04:00
|
|
|
}
|
|
|
|
|
2019-11-26 02:28:21 -05:00
|
|
|
static void
|
|
|
|
hash_table_remove_with_scales (GHashTable *hash,
|
|
|
|
GList *scales,
|
|
|
|
const char *base_key)
|
|
|
|
{
|
|
|
|
GList *l;
|
|
|
|
|
|
|
|
for (l = scales; l; l = l->next)
|
|
|
|
{
|
|
|
|
double scale = *((double *)l->data);
|
|
|
|
g_autofree char *key = NULL;
|
|
|
|
key = g_strdup_printf ("%s%f", base_key, scale);
|
|
|
|
g_hash_table_remove (hash, key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 16:51:38 -05:00
|
|
|
static void
|
|
|
|
hash_table_insert_scale (GHashTable *hash,
|
|
|
|
double scale)
|
|
|
|
{
|
|
|
|
double *saved_scale;
|
|
|
|
|
|
|
|
if (g_hash_table_contains (hash, &scale))
|
|
|
|
return;
|
|
|
|
|
|
|
|
saved_scale = g_new (double, 1);
|
|
|
|
*saved_scale = scale;
|
|
|
|
|
|
|
|
g_hash_table_add (hash, saved_scale);
|
|
|
|
}
|
|
|
|
|
2012-09-20 15:42:52 -04:00
|
|
|
static void
|
|
|
|
file_changed_cb (GFileMonitor *monitor,
|
|
|
|
GFile *file,
|
|
|
|
GFile *other,
|
|
|
|
GFileMonitorEvent event_type,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
StTextureCache *cache = user_data;
|
2014-09-18 20:04:00 -04:00
|
|
|
char *key;
|
|
|
|
guint file_hash;
|
2019-11-26 02:28:21 -05:00
|
|
|
g_autoptr (GList) scales = NULL;
|
2012-09-20 15:42:52 -04:00
|
|
|
|
2018-11-09 16:55:53 -05:00
|
|
|
if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
|
2012-09-20 15:42:52 -04:00
|
|
|
return;
|
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
file_hash = g_file_hash (file);
|
2019-11-26 02:28:21 -05:00
|
|
|
scales = g_hash_table_get_keys (cache->priv->used_scales);
|
2012-09-20 15:42:52 -04:00
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
key = g_strdup_printf (CACHE_PREFIX_FILE "%u", file_hash);
|
2012-09-20 15:42:52 -04:00
|
|
|
g_hash_table_remove (cache->priv->keyed_cache, key);
|
2019-11-26 02:28:21 -05:00
|
|
|
hash_table_remove_with_scales (cache->priv->keyed_cache, scales, key);
|
2012-09-20 15:42:52 -04:00
|
|
|
g_free (key);
|
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
key = g_strdup_printf (CACHE_PREFIX_FILE_FOR_CAIRO "%u", file_hash);
|
2018-04-17 05:53:02 -04:00
|
|
|
g_hash_table_remove (cache->priv->keyed_surface_cache, key);
|
2019-11-26 02:28:21 -05:00
|
|
|
hash_table_remove_with_scales (cache->priv->keyed_surface_cache, scales, key);
|
2012-09-20 15:42:52 -04:00
|
|
|
g_free (key);
|
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
g_signal_emit (cache, signals[TEXTURE_FILE_CHANGED], 0, file);
|
2012-09-20 15:42:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-09-18 20:04:00 -04:00
|
|
|
ensure_monitor_for_file (StTextureCache *cache,
|
|
|
|
GFile *file)
|
2012-09-20 15:42:52 -04:00
|
|
|
{
|
|
|
|
StTextureCachePrivate *priv = cache->priv;
|
|
|
|
|
2015-02-04 15:23:30 -05:00
|
|
|
/* No point in trying to monitor files that are part of a
|
|
|
|
* GResource, since it does not support file monitoring.
|
|
|
|
*/
|
|
|
|
if (g_file_has_uri_scheme (file, "resource"))
|
|
|
|
return;
|
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
if (g_hash_table_lookup (priv->file_monitors, file) == NULL)
|
2012-09-20 15:42:52 -04:00
|
|
|
{
|
|
|
|
GFileMonitor *monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
|
|
|
|
NULL, NULL);
|
|
|
|
g_signal_connect (monitor, "changed",
|
|
|
|
G_CALLBACK (file_changed_cb), cache);
|
2014-09-18 20:04:00 -04:00
|
|
|
g_hash_table_insert (priv->file_monitors, g_object_ref (file), monitor);
|
2012-09-20 15:42:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-05 05:00:23 -04:00
|
|
|
typedef struct {
|
2014-09-18 20:04:00 -04:00
|
|
|
GFile *gfile;
|
2011-10-05 05:00:23 -04:00
|
|
|
gint grid_width, grid_height;
|
2017-05-11 00:25:00 -04:00
|
|
|
gint paint_scale;
|
|
|
|
gfloat resource_scale;
|
2012-02-14 11:33:10 -05:00
|
|
|
ClutterActor *actor;
|
2017-11-29 20:48:02 -05:00
|
|
|
GCancellable *cancellable;
|
2012-11-05 17:11:27 -05:00
|
|
|
GFunc load_callback;
|
|
|
|
gpointer load_callback_data;
|
2011-10-05 05:00:23 -04:00
|
|
|
} AsyncImageData;
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_data_destroy (gpointer data)
|
|
|
|
{
|
|
|
|
AsyncImageData *d = (AsyncImageData *)data;
|
2014-09-18 20:04:00 -04:00
|
|
|
g_object_unref (d->gfile);
|
2012-02-14 11:33:10 -05:00
|
|
|
g_object_unref (d->actor);
|
2017-11-29 20:48:02 -05:00
|
|
|
g_object_unref (d->cancellable);
|
2020-10-19 14:12:27 -04:00
|
|
|
g_free (d);
|
2011-10-05 05:00:23 -04:00
|
|
|
}
|
|
|
|
|
2017-11-29 20:48:02 -05:00
|
|
|
static void
|
|
|
|
on_sliced_image_actor_destroyed (ClutterActor *actor,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
GTask *task = data;
|
|
|
|
GCancellable *cancellable = g_task_get_cancellable (task);
|
|
|
|
|
|
|
|
g_cancellable_cancel (cancellable);
|
|
|
|
}
|
|
|
|
|
2010-06-08 06:15:16 -04:00
|
|
|
static void
|
|
|
|
on_sliced_image_loaded (GObject *source_object,
|
|
|
|
GAsyncResult *res,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
2012-11-05 17:11:27 -05:00
|
|
|
GObject *cache = source_object;
|
2010-06-08 06:15:16 -04:00
|
|
|
AsyncImageData *data = (AsyncImageData *)user_data;
|
2015-09-25 14:25:41 -04:00
|
|
|
GTask *task = G_TASK (res);
|
2017-04-07 05:56:01 -04:00
|
|
|
GList *list, *pixbufs;
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2017-11-29 20:48:02 -05:00
|
|
|
if (g_task_had_error (task) || g_cancellable_is_cancelled (data->cancellable))
|
2010-06-08 06:15:16 -04:00
|
|
|
return;
|
|
|
|
|
2017-04-07 05:56:01 -04:00
|
|
|
pixbufs = g_task_propagate_pointer (task, NULL);
|
|
|
|
|
|
|
|
for (list = pixbufs; list; list = list->next)
|
2010-06-08 06:15:16 -04:00
|
|
|
{
|
2019-02-25 15:26:46 -05:00
|
|
|
ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data),
|
|
|
|
data->paint_scale,
|
|
|
|
data->resource_scale);
|
2010-06-08 06:15:16 -04:00
|
|
|
clutter_actor_hide (actor);
|
2012-02-14 11:33:10 -05:00
|
|
|
clutter_actor_add_child (data->actor, actor);
|
2010-06-08 06:15:16 -04:00
|
|
|
}
|
2012-11-05 17:11:27 -05:00
|
|
|
|
2017-04-07 05:56:01 -04:00
|
|
|
g_list_free_full (pixbufs, g_object_unref);
|
|
|
|
|
2017-11-29 20:48:02 -05:00
|
|
|
g_signal_handlers_disconnect_by_func (data->actor,
|
|
|
|
on_sliced_image_actor_destroyed,
|
|
|
|
task);
|
|
|
|
|
2012-11-05 17:11:27 -05:00
|
|
|
if (data->load_callback != NULL)
|
|
|
|
data->load_callback (cache, data->load_callback_data);
|
2010-06-08 06:15:16 -04:00
|
|
|
}
|
|
|
|
|
2010-12-05 16:46:48 -05:00
|
|
|
static void
|
|
|
|
free_glist_unref_gobjects (gpointer p)
|
|
|
|
{
|
2017-04-07 05:56:01 -04:00
|
|
|
g_list_free_full (p, g_object_unref);
|
2010-12-05 16:46:48 -05:00
|
|
|
}
|
|
|
|
|
2014-03-22 22:20:50 -04:00
|
|
|
static void
|
|
|
|
on_loader_size_prepared (GdkPixbufLoader *loader,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
AsyncImageData *data = user_data;
|
2017-05-11 00:25:00 -04:00
|
|
|
int scale = ceilf (data->paint_scale * data->resource_scale);
|
|
|
|
|
|
|
|
gdk_pixbuf_loader_set_size (loader, width * scale, height * scale);
|
2014-03-22 22:20:50 -04:00
|
|
|
}
|
|
|
|
|
2010-06-08 06:15:16 -04:00
|
|
|
static void
|
2015-09-25 14:25:41 -04:00
|
|
|
load_sliced_image (GTask *result,
|
|
|
|
gpointer object,
|
|
|
|
gpointer task_data,
|
2010-06-08 06:15:16 -04:00
|
|
|
GCancellable *cancellable)
|
|
|
|
{
|
|
|
|
AsyncImageData *data;
|
|
|
|
GList *res = NULL;
|
|
|
|
GdkPixbuf *pix;
|
|
|
|
gint width, height, y, x;
|
2017-05-11 00:25:00 -04:00
|
|
|
gint scale_factor;
|
2014-03-22 22:20:50 -04:00
|
|
|
GdkPixbufLoader *loader;
|
2016-11-22 12:12:35 -05:00
|
|
|
GError *error = NULL;
|
2014-03-22 22:20:50 -04:00
|
|
|
gchar *buffer = NULL;
|
|
|
|
gsize length;
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2017-11-29 20:48:02 -05:00
|
|
|
g_assert (cancellable);
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2015-09-25 14:25:41 -04:00
|
|
|
data = task_data;
|
2010-06-08 06:15:16 -04:00
|
|
|
g_assert (data);
|
|
|
|
|
2014-03-22 22:20:50 -04:00
|
|
|
loader = gdk_pixbuf_loader_new ();
|
|
|
|
g_signal_connect (loader, "size-prepared", G_CALLBACK (on_loader_size_prepared), data);
|
|
|
|
|
2017-11-29 20:48:02 -05:00
|
|
|
if (!g_file_load_contents (data->gfile, cancellable, &buffer, &length, NULL, &error))
|
2016-11-22 12:12:35 -05:00
|
|
|
{
|
|
|
|
g_warning ("Failed to open sliced image: %s", error->message);
|
|
|
|
goto out;
|
|
|
|
}
|
2014-03-22 22:20:50 -04:00
|
|
|
|
2016-11-22 12:12:35 -05:00
|
|
|
if (!gdk_pixbuf_loader_write (loader, (const guchar *) buffer, length, &error))
|
|
|
|
{
|
|
|
|
g_warning ("Failed to load image: %s", error->message);
|
|
|
|
goto out;
|
|
|
|
}
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2014-03-22 22:20:50 -04:00
|
|
|
if (!gdk_pixbuf_loader_close (loader, NULL))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pix = gdk_pixbuf_loader_get_pixbuf (loader);
|
2010-06-08 06:15:16 -04:00
|
|
|
width = gdk_pixbuf_get_width (pix);
|
|
|
|
height = gdk_pixbuf_get_height (pix);
|
2017-05-11 00:25:00 -04:00
|
|
|
scale_factor = ceilf (data->paint_scale * data->resource_scale);
|
|
|
|
for (y = 0; y < height; y += data->grid_height * scale_factor)
|
2010-06-08 06:15:16 -04:00
|
|
|
{
|
2017-05-11 00:25:00 -04:00
|
|
|
for (x = 0; x < width; x += data->grid_width * scale_factor)
|
2010-06-08 06:15:16 -04:00
|
|
|
{
|
2014-03-22 22:20:50 -04:00
|
|
|
GdkPixbuf *pixbuf = gdk_pixbuf_new_subpixbuf (pix, x, y,
|
2017-05-11 00:25:00 -04:00
|
|
|
data->grid_width * scale_factor,
|
|
|
|
data->grid_height * scale_factor);
|
2010-12-05 16:46:48 -05:00
|
|
|
g_assert (pixbuf != NULL);
|
2010-06-08 06:15:16 -04:00
|
|
|
res = g_list_append (res, pixbuf);
|
|
|
|
}
|
|
|
|
}
|
2014-03-22 22:20:50 -04:00
|
|
|
|
|
|
|
out:
|
|
|
|
/* We don't need the original pixbuf anymore, which is owned by the loader,
|
|
|
|
* though the subpixbufs will hold a reference. */
|
|
|
|
g_object_unref (loader);
|
|
|
|
g_free (buffer);
|
2016-11-22 12:12:35 -05:00
|
|
|
g_clear_pointer (&error, g_error_free);
|
2015-09-25 14:25:41 -04:00
|
|
|
g_task_return_pointer (result, res, free_glist_unref_gobjects);
|
2010-06-08 06:15:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* st_texture_cache_load_sliced_image:
|
|
|
|
* @cache: A #StTextureCache
|
2014-09-18 20:04:00 -04:00
|
|
|
* @file: A #GFile
|
2010-06-08 06:15:16 -04:00
|
|
|
* @grid_width: Width in pixels
|
|
|
|
* @grid_height: Height in pixels
|
2017-05-11 00:25:00 -04:00
|
|
|
* @paint_scale: Scale factor of the display
|
2014-05-28 15:54:02 -04:00
|
|
|
* @load_callback: (scope async) (nullable): Function called when the image is loaded, or %NULL
|
2012-11-05 17:11:27 -05:00
|
|
|
* @user_data: Data to pass to the load callback
|
2010-06-08 06:15:16 -04:00
|
|
|
*
|
|
|
|
* This function reads a single image file which contains multiple images internally.
|
|
|
|
* The image file will be divided using @grid_width and @grid_height;
|
2017-05-11 00:25:00 -04:00
|
|
|
* note that the dimensions of the image loaded from @path
|
2010-06-08 06:15:16 -04:00
|
|
|
* should be a multiple of the specified grid dimensions.
|
|
|
|
*
|
2012-02-14 11:33:10 -05:00
|
|
|
* Returns: (transfer none): A new #ClutterActor
|
2010-06-08 06:15:16 -04:00
|
|
|
*/
|
2012-02-14 11:33:10 -05:00
|
|
|
ClutterActor *
|
2012-11-05 17:11:27 -05:00
|
|
|
st_texture_cache_load_sliced_image (StTextureCache *cache,
|
2014-09-18 20:04:00 -04:00
|
|
|
GFile *file,
|
2012-11-05 17:11:27 -05:00
|
|
|
gint grid_width,
|
|
|
|
gint grid_height,
|
2017-05-11 00:25:00 -04:00
|
|
|
gint paint_scale,
|
|
|
|
gfloat resource_scale,
|
2012-11-05 17:11:27 -05:00
|
|
|
GFunc load_callback,
|
|
|
|
gpointer user_data)
|
2010-06-08 06:15:16 -04:00
|
|
|
{
|
|
|
|
AsyncImageData *data;
|
2015-09-25 14:25:41 -04:00
|
|
|
GTask *result;
|
2012-02-14 11:33:10 -05:00
|
|
|
ClutterActor *actor = clutter_actor_new ();
|
2017-11-29 20:48:02 -05:00
|
|
|
GCancellable *cancellable = g_cancellable_new ();
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2017-05-11 00:25:00 -04:00
|
|
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
|
|
|
g_assert (paint_scale > 0);
|
|
|
|
g_assert (resource_scale > 0);
|
|
|
|
|
2020-10-19 14:12:27 -04:00
|
|
|
data = g_new0 (AsyncImageData, 1);
|
2010-06-08 06:15:16 -04:00
|
|
|
data->grid_width = grid_width;
|
|
|
|
data->grid_height = grid_height;
|
2017-05-11 00:25:00 -04:00
|
|
|
data->paint_scale = paint_scale;
|
|
|
|
data->resource_scale = resource_scale;
|
2014-09-18 20:04:00 -04:00
|
|
|
data->gfile = g_object_ref (file);
|
2012-02-14 11:33:10 -05:00
|
|
|
data->actor = actor;
|
2017-11-29 20:48:02 -05:00
|
|
|
data->cancellable = cancellable;
|
2012-11-05 17:11:27 -05:00
|
|
|
data->load_callback = load_callback;
|
|
|
|
data->load_callback_data = user_data;
|
2012-02-14 11:33:10 -05:00
|
|
|
g_object_ref (G_OBJECT (actor));
|
2010-06-08 06:15:16 -04:00
|
|
|
|
2017-11-29 20:48:02 -05:00
|
|
|
result = g_task_new (cache, cancellable, on_sliced_image_loaded, data);
|
|
|
|
|
|
|
|
g_signal_connect (actor, "destroy",
|
|
|
|
G_CALLBACK (on_sliced_image_actor_destroyed), result);
|
|
|
|
|
2015-09-25 14:25:41 -04:00
|
|
|
g_task_set_task_data (result, data, on_data_destroy);
|
|
|
|
g_task_run_in_thread (result, load_sliced_image);
|
2010-06-08 06:15:16 -04:00
|
|
|
|
|
|
|
g_object_unref (result);
|
|
|
|
|
2012-02-14 11:33:10 -05:00
|
|
|
return actor;
|
2010-06-08 06:15:16 -04:00
|
|
|
}
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
/**
|
2014-09-18 20:04:00 -04:00
|
|
|
* st_texture_cache_load_file_async:
|
2020-05-20 19:50:09 -04:00
|
|
|
* @cache: A #StTextureCache
|
2014-09-18 20:04:00 -04:00
|
|
|
* @file: a #GFile of the image file from which to create a pixbuf
|
2010-02-09 12:42:07 -05:00
|
|
|
* @available_width: available width for the image, can be -1 if not limited
|
|
|
|
* @available_height: available height for the image, can be -1 if not limited
|
2017-05-11 00:25:00 -04:00
|
|
|
* @paint_scale: scale factor of the display
|
|
|
|
* @resource_scale: Resource scale factor
|
2010-02-09 12:42:07 -05:00
|
|
|
*
|
|
|
|
* Asynchronously load an image. Initially, the returned texture will have a natural
|
|
|
|
* size of zero. At some later point, either the image will be loaded successfully
|
|
|
|
* and at that point size will be negotiated, or upon an error, no image will be set.
|
|
|
|
*
|
2020-05-20 19:50:09 -04:00
|
|
|
* Returns: (transfer none): A new #ClutterActor with no image loaded initially.
|
2010-02-09 12:42:07 -05:00
|
|
|
*/
|
|
|
|
ClutterActor *
|
2014-09-18 20:04:00 -04:00
|
|
|
st_texture_cache_load_file_async (StTextureCache *cache,
|
|
|
|
GFile *file,
|
|
|
|
int available_width,
|
|
|
|
int available_height,
|
2017-05-11 00:25:00 -04:00
|
|
|
int paint_scale,
|
|
|
|
gfloat resource_scale)
|
2010-02-09 12:42:07 -05:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
ClutterActor *actor;
|
2012-03-16 23:48:22 -04:00
|
|
|
AsyncTextureLoadData *request;
|
|
|
|
StTextureCachePolicy policy;
|
|
|
|
gchar *key;
|
2017-05-11 00:25:00 -04:00
|
|
|
int scale;
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2017-05-11 00:25:00 -04:00
|
|
|
scale = ceilf (paint_scale * resource_scale);
|
|
|
|
key = g_strdup_printf (CACHE_PREFIX_FILE "%u%d", g_file_hash (file), scale);
|
2012-03-16 23:48:22 -04:00
|
|
|
|
|
|
|
policy = ST_TEXTURE_CACHE_POLICY_NONE; /* XXX */
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
actor = create_invisible_actor ();
|
2012-03-16 23:48:22 -04:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
if (ensure_request (cache, key, policy, &request, actor))
|
2012-03-16 23:48:22 -04:00
|
|
|
{
|
|
|
|
/* If there's an outstanding request, we've just added ourselves to it */
|
|
|
|
g_free (key);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Else, make a new request */
|
|
|
|
|
|
|
|
request->cache = cache;
|
|
|
|
/* Transfer ownership of key */
|
|
|
|
request->key = key;
|
2014-09-18 20:04:00 -04:00
|
|
|
request->file = g_object_ref (file);
|
2012-03-16 23:48:22 -04:00
|
|
|
request->policy = policy;
|
|
|
|
request->width = available_width;
|
|
|
|
request->height = available_height;
|
2017-05-11 00:25:00 -04:00
|
|
|
request->paint_scale = paint_scale;
|
|
|
|
request->resource_scale = resource_scale;
|
2012-03-16 23:48:22 -04:00
|
|
|
|
|
|
|
load_texture_async (cache, request);
|
|
|
|
}
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
ensure_monitor_for_file (cache, file);
|
2012-09-20 15:42:52 -04:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
return actor;
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2014-08-07 14:40:01 -04:00
|
|
|
static CoglTexture *
|
2014-09-18 20:04:00 -04:00
|
|
|
st_texture_cache_load_file_sync_to_cogl_texture (StTextureCache *cache,
|
|
|
|
StTextureCachePolicy policy,
|
|
|
|
GFile *file,
|
|
|
|
int available_width,
|
|
|
|
int available_height,
|
2017-05-11 00:25:00 -04:00
|
|
|
int paint_scale,
|
|
|
|
gfloat resource_scale,
|
2014-09-18 20:04:00 -04:00
|
|
|
GError **error)
|
2010-02-09 13:24:33 -05:00
|
|
|
{
|
2019-01-24 10:42:14 -05:00
|
|
|
ClutterContent *image;
|
2014-08-07 14:40:01 -04:00
|
|
|
CoglTexture *texdata;
|
2010-02-09 13:24:33 -05:00
|
|
|
GdkPixbuf *pixbuf;
|
2010-02-09 15:12:28 -05:00
|
|
|
char *key;
|
2010-02-09 13:24:33 -05:00
|
|
|
|
2017-05-11 00:25:00 -04:00
|
|
|
key = g_strdup_printf (CACHE_PREFIX_FILE "%u%f", g_file_hash (file), resource_scale);
|
2010-02-09 13:24:33 -05:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
texdata = NULL;
|
|
|
|
image = g_hash_table_lookup (cache->priv->keyed_cache, key);
|
2010-02-09 13:24:33 -05:00
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
if (image == NULL)
|
2010-02-09 13:24:33 -05:00
|
|
|
{
|
2017-05-11 00:25:00 -04:00
|
|
|
pixbuf = impl_load_pixbuf_file (file, available_width, available_height,
|
|
|
|
paint_scale, resource_scale, error);
|
2010-02-09 13:24:33 -05:00
|
|
|
if (!pixbuf)
|
2010-02-09 15:12:28 -05:00
|
|
|
goto out;
|
2010-02-09 13:24:33 -05:00
|
|
|
|
2019-02-26 21:18:39 -05:00
|
|
|
image = pixbuf_to_st_content_image (pixbuf,
|
|
|
|
available_height, available_width,
|
|
|
|
paint_scale, resource_scale);
|
2010-02-09 13:24:33 -05:00
|
|
|
g_object_unref (pixbuf);
|
|
|
|
|
2019-01-24 10:42:14 -05:00
|
|
|
if (!image)
|
2018-04-17 05:43:34 -04:00
|
|
|
goto out;
|
|
|
|
|
2010-02-09 13:24:33 -05:00
|
|
|
if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
|
2019-11-26 02:28:21 -05:00
|
|
|
{
|
|
|
|
g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), image);
|
2020-02-20 16:51:38 -05:00
|
|
|
hash_table_insert_scale (cache->priv->used_scales, (double)resource_scale);
|
2019-11-26 02:28:21 -05:00
|
|
|
}
|
2010-02-09 13:24:33 -05:00
|
|
|
}
|
2019-01-24 10:42:14 -05:00
|
|
|
|
|
|
|
/* Because the texture is loaded synchronously, we won't call
|
|
|
|
* clutter_image_set_data(), so it's safe to use the texture
|
|
|
|
* of ClutterImage here. */
|
|
|
|
texdata = clutter_image_get_texture (CLUTTER_IMAGE (image));
|
|
|
|
cogl_object_ref (texdata);
|
2010-02-09 13:24:33 -05:00
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
ensure_monitor_for_file (cache, file);
|
2012-09-20 15:42:52 -04:00
|
|
|
|
2010-02-09 15:12:28 -05:00
|
|
|
out:
|
|
|
|
g_free (key);
|
2010-02-09 13:24:33 -05:00
|
|
|
return texdata;
|
|
|
|
}
|
|
|
|
|
2010-12-10 17:15:39 -05:00
|
|
|
static cairo_surface_t *
|
2014-09-18 20:04:00 -04:00
|
|
|
st_texture_cache_load_file_sync_to_cairo_surface (StTextureCache *cache,
|
|
|
|
StTextureCachePolicy policy,
|
|
|
|
GFile *file,
|
|
|
|
int available_width,
|
|
|
|
int available_height,
|
2017-05-11 00:25:00 -04:00
|
|
|
int paint_scale,
|
|
|
|
gfloat resource_scale,
|
2014-09-18 20:04:00 -04:00
|
|
|
GError **error)
|
2010-12-10 17:15:39 -05:00
|
|
|
{
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
char *key;
|
|
|
|
|
2017-05-11 00:25:00 -04:00
|
|
|
key = g_strdup_printf (CACHE_PREFIX_FILE_FOR_CAIRO "%u%f", g_file_hash (file), resource_scale);
|
2010-12-10 17:15:39 -05:00
|
|
|
|
2018-04-17 05:53:02 -04:00
|
|
|
surface = g_hash_table_lookup (cache->priv->keyed_surface_cache, key);
|
2010-12-10 17:15:39 -05:00
|
|
|
|
|
|
|
if (surface == NULL)
|
|
|
|
{
|
2017-05-11 00:25:00 -04:00
|
|
|
pixbuf = impl_load_pixbuf_file (file, available_width, available_height,
|
|
|
|
paint_scale, resource_scale, error);
|
2010-12-10 17:15:39 -05:00
|
|
|
if (!pixbuf)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
surface = pixbuf_to_cairo_surface (pixbuf);
|
|
|
|
g_object_unref (pixbuf);
|
|
|
|
|
|
|
|
if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
|
|
|
|
{
|
|
|
|
cairo_surface_reference (surface);
|
2018-04-17 05:53:02 -04:00
|
|
|
g_hash_table_insert (cache->priv->keyed_surface_cache,
|
|
|
|
g_strdup (key), surface);
|
2020-02-20 16:51:38 -05:00
|
|
|
hash_table_insert_scale (cache->priv->used_scales, (double)resource_scale);
|
2010-12-10 17:15:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cairo_surface_reference (surface);
|
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
ensure_monitor_for_file (cache, file);
|
2012-09-20 15:42:52 -04:00
|
|
|
|
2010-12-10 17:15:39 -05:00
|
|
|
out:
|
|
|
|
g_free (key);
|
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
2010-02-09 13:24:33 -05:00
|
|
|
/**
|
2014-08-07 14:40:01 -04:00
|
|
|
* st_texture_cache_load_file_to_cogl_texture: (skip)
|
2010-02-09 13:24:33 -05:00
|
|
|
* @cache: A #StTextureCache
|
2014-09-18 20:04:00 -04:00
|
|
|
* @file: A #GFile in supported image format
|
2017-05-11 00:25:00 -04:00
|
|
|
* @paint_scale: Scale factor of the display
|
|
|
|
* @resource_scale: Resource scale factor
|
2010-02-09 13:24:33 -05:00
|
|
|
*
|
|
|
|
* This function synchronously loads the given file path
|
|
|
|
* into a COGL texture. On error, a warning is emitted
|
2014-08-07 14:40:01 -04:00
|
|
|
* and %NULL is returned.
|
2010-09-01 20:48:11 -04:00
|
|
|
*
|
2014-08-07 14:40:01 -04:00
|
|
|
* Returns: (transfer full): a new #CoglTexture
|
2010-02-09 13:24:33 -05:00
|
|
|
*/
|
2014-08-07 14:40:01 -04:00
|
|
|
CoglTexture *
|
2010-02-09 13:24:33 -05:00
|
|
|
st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache,
|
2014-09-18 20:04:00 -04:00
|
|
|
GFile *file,
|
2017-05-11 00:25:00 -04:00
|
|
|
gint paint_scale,
|
|
|
|
gfloat resource_scale)
|
2010-02-09 13:24:33 -05:00
|
|
|
{
|
2014-08-07 14:40:01 -04:00
|
|
|
CoglTexture *texture;
|
2010-02-09 13:24:33 -05:00
|
|
|
GError *error = NULL;
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
texture = st_texture_cache_load_file_sync_to_cogl_texture (cache, ST_TEXTURE_CACHE_POLICY_FOREVER,
|
2017-05-11 00:25:00 -04:00
|
|
|
file, -1, -1, paint_scale, resource_scale,
|
|
|
|
&error);
|
2010-02-09 12:42:07 -05:00
|
|
|
|
2010-02-09 13:24:33 -05:00
|
|
|
if (texture == NULL)
|
|
|
|
{
|
2014-09-18 20:04:00 -04:00
|
|
|
char *uri = g_file_get_uri (file);
|
|
|
|
g_warning ("Failed to load %s: %s", uri, error->message);
|
2010-02-09 13:24:33 -05:00
|
|
|
g_clear_error (&error);
|
2014-09-18 20:04:00 -04:00
|
|
|
g_free (uri);
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
2014-09-18 20:04:00 -04:00
|
|
|
|
2010-02-09 13:24:33 -05:00
|
|
|
return texture;
|
2010-02-09 12:42:07 -05:00
|
|
|
}
|
2009-09-08 15:47:30 -04:00
|
|
|
|
2010-12-10 17:15:39 -05:00
|
|
|
/**
|
|
|
|
* st_texture_cache_load_file_to_cairo_surface:
|
|
|
|
* @cache: A #StTextureCache
|
2014-09-18 20:04:00 -04:00
|
|
|
* @file: A #GFile in supported image format
|
2017-05-11 00:25:00 -04:00
|
|
|
* @paint_scale: Scale factor of the display
|
|
|
|
* @resource_scale: Resource scale factor
|
2010-12-10 17:15:39 -05:00
|
|
|
*
|
|
|
|
* This function synchronously loads the given file path
|
|
|
|
* into a cairo surface. On error, a warning is emitted
|
|
|
|
* and %NULL is returned.
|
|
|
|
*
|
2011-10-17 22:09:48 -04:00
|
|
|
* Returns: (transfer full): a new #cairo_surface_t
|
2010-12-10 17:15:39 -05:00
|
|
|
*/
|
|
|
|
cairo_surface_t *
|
|
|
|
st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache,
|
2014-09-18 20:04:00 -04:00
|
|
|
GFile *file,
|
2017-05-11 00:25:00 -04:00
|
|
|
gint paint_scale,
|
|
|
|
gfloat resource_scale)
|
2010-12-10 17:15:39 -05:00
|
|
|
{
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
2014-09-18 20:04:00 -04:00
|
|
|
surface = st_texture_cache_load_file_sync_to_cairo_surface (cache, ST_TEXTURE_CACHE_POLICY_FOREVER,
|
2017-05-11 00:25:00 -04:00
|
|
|
file, -1, -1, paint_scale, resource_scale,
|
|
|
|
&error);
|
2010-12-10 17:15:39 -05:00
|
|
|
|
|
|
|
if (surface == NULL)
|
|
|
|
{
|
2014-09-18 20:04:00 -04:00
|
|
|
char *uri = g_file_get_uri (file);
|
|
|
|
g_warning ("Failed to load %s: %s", uri, error->message);
|
2010-12-10 17:15:39 -05:00
|
|
|
g_clear_error (&error);
|
2014-09-18 20:04:00 -04:00
|
|
|
g_free (uri);
|
2010-12-10 17:15:39 -05:00
|
|
|
}
|
2014-09-18 20:04:00 -04:00
|
|
|
|
2010-12-10 17:15:39 -05:00
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
2010-02-09 12:42:07 -05:00
|
|
|
static StTextureCache *instance = NULL;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* st_texture_cache_get_default:
|
|
|
|
*
|
2020-05-20 19:50:09 -04:00
|
|
|
* Returns: (transfer none): The global texture cache
|
2010-02-09 12:42:07 -05:00
|
|
|
*/
|
|
|
|
StTextureCache*
|
|
|
|
st_texture_cache_get_default (void)
|
|
|
|
{
|
|
|
|
if (instance == NULL)
|
|
|
|
instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
|
|
|
|
return instance;
|
|
|
|
}
|
2019-08-01 19:58:20 -04:00
|
|
|
|
2020-05-20 19:50:09 -04:00
|
|
|
/**
|
|
|
|
* st_texture_cache_rescan_icon_theme:
|
|
|
|
*
|
|
|
|
* Rescan the current icon theme, if necessary.
|
|
|
|
*
|
|
|
|
* Returns: %TRUE if the icon theme has changed and needed to be reloaded.
|
|
|
|
*/
|
2019-08-01 19:58:20 -04:00
|
|
|
gboolean
|
|
|
|
st_texture_cache_rescan_icon_theme (StTextureCache *cache)
|
|
|
|
{
|
|
|
|
StTextureCachePrivate *priv = cache->priv;
|
|
|
|
|
|
|
|
return gtk_icon_theme_rescan_if_needed (priv->icon_theme);
|
|
|
|
}
|