gnome-shell-3.6.3.1/src/st/st-texture-cache.c

Location Tool Test ID Function Issue
st/st-texture-cache.c:67:3 gcc deprecated-declarations create_default_texture 'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead*
st/st-texture-cache.c:76:3 gcc deprecated-declarations set_texture_cogl_texture 'clutter_texture_set_cogl_texture' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:80)*
st/st-texture-cache.c:755:7 gcc deprecated-declarations st_texture_cache_reset_texture 'clutter_texture_set_cogl_texture' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:80)
st/st-texture-cache.c:815:3 gcc deprecated-declarations st_texture_cache_bind_pixbuf_property 'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
st/st-texture-cache.c:1360:7 clang-analyzer Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
st/st-texture-cache.c:1397:7 clang-analyzer Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
   1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
   2 /*
   3  * st-texture-cache.h: Object for loading and caching images as textures
   4  *
   5  * Copyright 2009, 2010 Red Hat, Inc.
   6  * Copyright 2010, Maxim Ermilov
   7  *
   8  * This program is free software; you can redistribute it and/or modify
   9  * it under the terms of the GNU Lesser General Public License as
  10  * published by the Free Software Foundation, either version 2.1 of
  11  * the License, or (at your option) any later version.
  12  *
  13  * This program is distributed in the hope it will be useful, but WITHOUT ANY
  14  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  16  * more details.
  17  *
  18  * You should have received a copy of the GNU Lesser General Public License
  19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20  */
  21 
  22 #include "config.h"
  23 
  24 #include "st-texture-cache.h"
  25 #include "st-private.h"
  26 #include <gtk/gtk.h>
  27 #include <string.h>
  28 #include <glib.h>
  29 
  30 #define CACHE_PREFIX_ICON "icon:"
  31 #define CACHE_PREFIX_URI "uri:"
  32 #define CACHE_PREFIX_URI_FOR_CAIRO "uri-for-cairo:"
  33 #define CACHE_PREFIX_RAW_CHECKSUM "raw-checksum:"
  34 #define CACHE_PREFIX_COMPRESSED_CHECKSUM "compressed-checksum:"
  35 
  36 struct _StTextureCachePrivate
  37 {
  38   GtkIconTheme *icon_theme;
  39 
  40   /* Things that were loaded with a cache policy != NONE */
  41   GHashTable *keyed_cache; /* char * -> CoglTexture* */
  42 
  43   /* Presently this is used to de-duplicate requests for GIcons and async URIs. */
  44   GHashTable *outstanding_requests; /* char * -> AsyncTextureLoadData * */
  45 };
  46 
  47 static void st_texture_cache_dispose (GObject *object);
  48 static void st_texture_cache_finalize (GObject *object);
  49 
  50 enum
  51 {
  52   ICON_THEME_CHANGED,
  53 
  54   LAST_SIGNAL
  55 };
  56 
  57 static guint signals[LAST_SIGNAL] = { 0, };
  58 G_DEFINE_TYPE(StTextureCache, st_texture_cache, G_TYPE_OBJECT);
  59 
  60 /* We want to preserve the aspect ratio by default, also the default
  61  * material for an empty texture is full opacity white, which we
  62  * definitely don't want.  Skip that by setting 0 opacity.
  63  */
  64 static ClutterTexture *
  65 create_default_texture (void)
  66 {
  67   ClutterTexture * texture = CLUTTER_TEXTURE (clutter_texture_new ());
  68   g_object_set (texture, "keep-aspect-ratio", TRUE, "opacity", 0, NULL);
  69   return texture;
  70 }
  71 
  72 /* Reverse the opacity we added while loading */
  73 static void
  74 set_texture_cogl_texture (ClutterTexture *clutter_texture, CoglHandle cogl_texture)
  75 {
  76   clutter_texture_set_cogl_texture (clutter_texture, cogl_texture);
  77   g_object_set (clutter_texture, "opacity", 255, NULL);
  78 }
  79 
  80 static void
  81 st_texture_cache_class_init (StTextureCacheClass *klass)
  82 {
  83   GObjectClass *gobject_class = (GObjectClass *)klass;
  84 
  85   gobject_class->dispose = st_texture_cache_dispose;
  86   gobject_class->finalize = st_texture_cache_finalize;
  87 
  88   signals[ICON_THEME_CHANGED] =
  89     g_signal_new ("icon-theme-changed",
  90                   G_TYPE_FROM_CLASS (klass),
  91                   G_SIGNAL_RUN_LAST,
  92                   0, /* no default handler slot */
  93                   NULL, NULL, NULL,
  94                   G_TYPE_NONE, 0);
  95 }
  96 
  97 /**
  98  * st_texture_cache_clear_uri:
  99  * @cache: A #StTextureCache
 100  * @uri: URI of cached object
 101  *
 102  * If the given @uri is known to have been modified
 103  * externally, this function may be used to invalidate
 104  * the in-memory cache.
 105  */
 106 void
 107 st_texture_cache_clear_uri (StTextureCache *cache,
 108                             const char     *uri)
 109 {
 110   char *key;
 111 
 112   g_return_if_fail (ST_IS_TEXTURE_CACHE (cache));
 113   g_return_if_fail (uri != NULL);
 114 
 115   key = g_strconcat (CACHE_PREFIX_URI, uri, NULL);
 116   g_hash_table_remove (cache->priv->keyed_cache, key);
 117   g_free (key);
 118 
 119   key = g_strconcat (CACHE_PREFIX_URI_FOR_CAIRO, uri, NULL);
 120   g_hash_table_remove (cache->priv->keyed_cache, key);
 121   g_free (key);
 122 }
 123 
 124 /* Evicts all cached textures for named icons */
 125 static void
 126 st_texture_cache_evict_icons (StTextureCache *cache)
 127 {
 128   GHashTableIter iter;
 129   gpointer key;
 130   gpointer value;
 131 
 132   g_hash_table_iter_init (&iter, cache->priv->keyed_cache);
 133   while (g_hash_table_iter_next (&iter, &key, &value))
 134     {
 135       const char *cache_key = key;
 136 
 137       /* This is too conservative - it takes out all cached textures
 138        * for GIcons even when they aren't named icons, but it's not
 139        * worth the complexity of parsing the key and calling
 140        * g_icon_new_for_string(); icon theme changes aren't normal */
 141       if (g_str_has_prefix (cache_key, CACHE_PREFIX_ICON))
 142         g_hash_table_iter_remove (&iter);
 143     }
 144 }
 145 
 146 static void
 147 on_icon_theme_changed (GtkIconTheme   *icon_theme,
 148                        StTextureCache *cache)
 149 {
 150   st_texture_cache_evict_icons (cache);
 151   g_signal_emit (cache, signals[ICON_THEME_CHANGED], 0);
 152 }
 153 
 154 static void
 155 st_texture_cache_init (StTextureCache *self)
 156 {
 157   self->priv = g_new0 (StTextureCachePrivate, 1);
 158 
 159   self->priv->icon_theme = gtk_icon_theme_get_default ();
 160   g_signal_connect (self->priv->icon_theme, "changed",
 161                     G_CALLBACK (on_icon_theme_changed), self);
 162 
 163   self->priv->keyed_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
 164                                                    g_free, cogl_handle_unref);
 165   self->priv->outstanding_requests = g_hash_table_new_full (g_str_hash, g_str_equal,
 166                                                             g_free, NULL);
 167 }
 168 
 169 static void
 170 st_texture_cache_dispose (GObject *object)
 171 {
 172   StTextureCache *self = (StTextureCache*)object;
 173 
 174   if (self->priv->icon_theme)
 175     {
 176       g_signal_handlers_disconnect_by_func (self->priv->icon_theme,
 177                                             (gpointer) on_icon_theme_changed,
 178                                             self);
 179       self->priv->icon_theme = NULL;
 180     }
 181 
 182   if (self->priv->keyed_cache)
 183     g_hash_table_destroy (self->priv->keyed_cache);
 184   self->priv->keyed_cache = NULL;
 185 
 186   if (self->priv->outstanding_requests)
 187     g_hash_table_destroy (self->priv->outstanding_requests);
 188   self->priv->outstanding_requests = NULL;
 189 
 190   G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
 191 }
 192 
 193 static void
 194 st_texture_cache_finalize (GObject *object)
 195 {
 196   G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
 197 }
 198 
 199 static gboolean
 200 compute_pixbuf_scale (gint      width,
 201                       gint      height,
 202                       gint      available_width,
 203                       gint      available_height,
 204                       gint     *new_width,
 205                       gint     *new_height)
 206 {
 207   int scaled_width, scaled_height;
 208 
 209   if (width == 0 || height == 0)
 210     return FALSE;
 211 
 212   if (available_width >= 0 && available_height >= 0)
 213     {
 214       /* This should keep the aspect ratio of the image intact, because if
 215        * available_width < (available_height * width) / height
 216        * then
 217        * (available_width * height) / width < available_height
 218        * So we are guaranteed to either scale the image to have an available_width
 219        * for width and height scaled accordingly OR have the available_height
 220        * for height and width scaled accordingly, whichever scaling results
 221        * in the image that can fit both available dimensions.
 222        */
 223       scaled_width = MIN (available_width, (available_height * width) / height);
 224       scaled_height = MIN (available_height, (available_width * height) / width);
 225     }
 226   else if (available_width >= 0)
 227     {
 228       scaled_width = available_width;
 229       scaled_height = (available_width * height) / width;
 230     }
 231   else if (available_height >= 0)
 232     {
 233       scaled_width = (available_height * width) / height;
 234       scaled_height = available_height;
 235     }
 236   else
 237     {
 238       scaled_width = scaled_height = 0;
 239     }
 240 
 241   /* Scale the image only if that will not increase its original dimensions. */
 242   if (scaled_width > 0 && scaled_height > 0 && scaled_width < width && scaled_height < height)
 243     {
 244       *new_width = scaled_width;
 245       *new_height = scaled_height;
 246       return TRUE;
 247     }
 248   return FALSE;
 249 }
 250 
 251 static void
 252 rgba_from_clutter (GdkRGBA      *rgba,
 253                    ClutterColor *color)
 254 {
 255   rgba->red = color->red / 255.;
 256   rgba->green = color->green / 255.;
 257   rgba->blue = color->blue / 255.;
 258   rgba->alpha = color->alpha / 255.;
 259 }
 260 
 261 static GdkPixbuf *
 262 impl_load_pixbuf_gicon (GtkIconInfo  *info,
 263                         int           size,
 264                         StIconColors *colors,
 265                         GError      **error)
 266 {
 267   int scaled_width, scaled_height;
 268   GdkPixbuf *pixbuf;
 269   int width, height;
 270 
 271   if (colors)
 272     {
 273       GdkRGBA foreground_color;
 274       GdkRGBA success_color;
 275       GdkRGBA warning_color;
 276       GdkRGBA error_color;
 277 
 278       rgba_from_clutter (&foreground_color, &colors->foreground);
 279       rgba_from_clutter (&success_color, &colors->success);
 280       rgba_from_clutter (&warning_color, &colors->warning);
 281       rgba_from_clutter (&error_color, &colors->error);
 282 
 283       pixbuf = gtk_icon_info_load_symbolic (info,
 284                                             &foreground_color, &success_color,
 285                                             &warning_color, &error_color,
 286                                             NULL, error);
 287     }
 288   else
 289     {
 290       pixbuf = gtk_icon_info_load_icon (info, error);
 291     }
 292 
 293   if (!pixbuf)
 294     return NULL;
 295 
 296   width = gdk_pixbuf_get_width (pixbuf);
 297   height = gdk_pixbuf_get_height (pixbuf);
 298 
 299   if (compute_pixbuf_scale (width,
 300                             height,
 301                             size, size,
 302                             &scaled_width, &scaled_height))
 303     {
 304       GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
 305       g_object_unref (pixbuf);
 306       pixbuf = scaled;
 307     }
 308   return pixbuf;
 309 }
 310 
 311 /* A private structure for keeping width and height. */
 312 typedef struct {
 313   int width;
 314   int height;
 315 } Dimensions;
 316 
 317 /* This struct corresponds to a request for an texture.
 318  * It's creasted when something needs a new texture,
 319  * and destroyed when the texture data is loaded. */
 320 typedef struct {
 321   StTextureCache *cache;
 322   StTextureCachePolicy policy;
 323   char *key;
 324 
 325   gboolean enforced_square;
 326 
 327   guint width;
 328   guint height;
 329   GSList *textures;
 330 
 331   GtkIconInfo *icon_info;
 332   StIconColors *colors;
 333   char *uri;
 334 } AsyncTextureLoadData;
 335 
 336 static void
 337 texture_load_data_destroy (gpointer p)
 338 {
 339   AsyncTextureLoadData *data = p;
 340 
 341   if (data->icon_info)
 342     {
 343       gtk_icon_info_free (data->icon_info);
 344       if (data->colors)
 345         st_icon_colors_unref (data->colors);
 346     }
 347   else if (data->uri)
 348     g_free (data->uri);
 349 
 350   if (data->key)
 351     g_free (data->key);
 352 
 353   if (data->textures)
 354     g_slist_free_full (data->textures, (GDestroyNotify) g_object_unref);
 355 }
 356 
 357 /**
 358  * on_image_size_prepared:
 359  * @pixbuf_loader: #GdkPixbufLoader loading the image
 360  * @width: the original width of the image
 361  * @height: the original height of the image
 362  * @data: pointer to the #Dimensions sructure containing available width and height for the image,
 363  *        available width or height can be -1 if the dimension is not limited
 364  *
 365  * Private function.
 366  *
 367  * Sets the size of the image being loaded to fit the available width and height dimensions,
 368  * but never scales up the image beyond its actual size.
 369  * Intended to be used as a callback for #GdkPixbufLoader "size-prepared" signal.
 370  */
 371 static void
 372 on_image_size_prepared (GdkPixbufLoader *pixbuf_loader,
 373                         gint             width,
 374                         gint             height,
 375                         gpointer         data)
 376 {
 377   Dimensions *available_dimensions = data;
 378   int available_width = available_dimensions->width;
 379   int available_height = available_dimensions->height;
 380   int scaled_width;
 381   int scaled_height;
 382 
 383   if (compute_pixbuf_scale (width, height, available_width, available_height,
 384                             &scaled_width, &scaled_height))
 385     gdk_pixbuf_loader_set_size (pixbuf_loader, scaled_width, scaled_height);
 386 }
 387 
 388 static GdkPixbuf *
 389 impl_load_pixbuf_data (const guchar   *data,
 390                        gsize           size,
 391                        int             available_width,
 392                        int             available_height,
 393                        GError        **error)
 394 {
 395   GdkPixbufLoader *pixbuf_loader = NULL;
 396   GdkPixbuf *rotated_pixbuf = NULL;
 397   GdkPixbuf *pixbuf;
 398   gboolean success;
 399   Dimensions available_dimensions;
 400   int width_before_rotation, width_after_rotation;
 401 
 402   pixbuf_loader = gdk_pixbuf_loader_new ();
 403 
 404   available_dimensions.width = available_width;
 405   available_dimensions.height = available_height;
 406   g_signal_connect (pixbuf_loader, "size-prepared",
 407                     G_CALLBACK (on_image_size_prepared), &available_dimensions);
 408 
 409   success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
 410   if (!success)
 411     goto out;
 412   success = gdk_pixbuf_loader_close (pixbuf_loader, error);
 413   if (!success)
 414     goto out;
 415 
 416   pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
 417 
 418   width_before_rotation = gdk_pixbuf_get_width (pixbuf);
 419 
 420   rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
 421   width_after_rotation = gdk_pixbuf_get_width (rotated_pixbuf);
 422 
 423   /* There is currently no way to tell if the pixbuf will need to be rotated before it is loaded,
 424    * so we only check that once it is loaded, and reload it again if it needs to be rotated in order
 425    * to use the available width and height correctly.
 426    * See http://bugzilla.gnome.org/show_bug.cgi?id=579003
 427    */
 428   if (width_before_rotation != width_after_rotation)
 429     {
 430       g_object_unref (pixbuf_loader);
 431       g_object_unref (rotated_pixbuf);
 432       rotated_pixbuf = NULL;
 433 
 434       pixbuf_loader = gdk_pixbuf_loader_new ();
 435 
 436       /* We know that the image will later be rotated, so we reverse the available dimensions. */
 437       available_dimensions.width = available_height;
 438       available_dimensions.height = available_width;
 439       g_signal_connect (pixbuf_loader, "size-prepared",
 440                         G_CALLBACK (on_image_size_prepared), &available_dimensions);
 441 
 442       success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
 443       if (!success)
 444         goto out;
 445 
 446       success = gdk_pixbuf_loader_close (pixbuf_loader, error);
 447       if (!success)
 448         goto out;
 449 
 450       pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
 451 
 452       rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
 453     }
 454 
 455 out:
 456   if (pixbuf_loader)
 457     g_object_unref (pixbuf_loader);
 458   return rotated_pixbuf;
 459 }
 460 
 461 static GdkPixbuf*
 462 decode_image (const char *val)
 463 {
 464   int i;
 465   GError *error = NULL;
 466   GdkPixbuf *res = NULL;
 467   struct {
 468     const char *prefix;
 469     const char *mime_type;
 470   } formats[] = {
 471     { "data:image/x-icon;base64,", "image/x-icon" },
 472     { "data:image/png;base64,", "image/png" }
 473   };
 474 
 475   g_return_val_if_fail (val, NULL);
 476 
 477   for (i = 0; i < G_N_ELEMENTS (formats); i++)
 478     {
 479       if (g_str_has_prefix (val, formats[i].prefix))
 480         {
 481           gsize len;
 482           guchar *data = NULL;
 483           char *unescaped;
 484 
 485           unescaped = g_uri_unescape_string (val + strlen (formats[i].prefix), NULL);
 486           if (unescaped)
 487             {
 488               data = g_base64_decode (unescaped, &len);
 489               g_free (unescaped);
 490             }
 491 
 492           if (data)
 493             {
 494               GdkPixbufLoader *loader;
 495 
 496               loader = gdk_pixbuf_loader_new_with_mime_type (formats[i].mime_type, &error);
 497               if (loader &&
 498                   gdk_pixbuf_loader_write (loader, data, len, &error) &&
 499                   gdk_pixbuf_loader_close (loader, &error))
 500                 {
 501                   res = gdk_pixbuf_loader_get_pixbuf (loader);
 502                   g_object_ref (res);
 503                 }
 504               g_object_unref (loader);
 505               g_free (data);
 506             }
 507         }
 508     }
 509   if (!res)
 510     {
 511       if (error)
 512         {
 513           g_warning ("%s\n", error->message);
 514           g_error_free (error);
 515         }
 516       else
 517         g_warning ("incorrect data uri");
 518     }
 519   return res;
 520 }
 521 
 522 static GdkPixbuf *
 523 impl_load_pixbuf_file (const char     *uri,
 524                        int             available_width,
 525                        int             available_height,
 526                        GError        **error)
 527 {
 528   GdkPixbuf *pixbuf = NULL;
 529   GFile *file;
 530   char *contents = NULL;
 531   gsize size;
 532 
 533   if (g_str_has_prefix (uri, "data:"))
 534     return decode_image (uri);
 535 
 536   file = g_file_new_for_uri (uri);
 537   if (g_file_load_contents (file, NULL, &contents, &size, NULL, error))
 538     {
 539       pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size,
 540                                       available_width, available_height,
 541                                       error);
 542     }
 543 
 544   g_object_unref (file);
 545   g_free (contents);
 546 
 547   return pixbuf;
 548 }
 549 
 550 static void
 551 load_pixbuf_thread (GSimpleAsyncResult *result,
 552                     GObject *object,
 553                     GCancellable *cancellable)
 554 {
 555   GdkPixbuf *pixbuf;
 556   AsyncTextureLoadData *data;
 557   GError *error = NULL;
 558 
 559   data = g_async_result_get_user_data (G_ASYNC_RESULT (result));
 560   g_assert (data != NULL);
 561 
 562   if (data->uri)
 563     pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error);
 564   else if (data->icon_info)
 565     pixbuf = impl_load_pixbuf_gicon (data->icon_info, data->width, data->colors, &error);
 566   else
 567     g_assert_not_reached ();
 568 
 569   if (error != NULL)
 570     {
 571       g_simple_async_result_set_from_error (result, error);
 572       return;
 573     }
 574 
 575   if (pixbuf)
 576     g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf),
 577                                                g_object_unref);
 578 }
 579 
 580 static GdkPixbuf *
 581 load_pixbuf_async_finish (StTextureCache *cache, GAsyncResult *result, GError **error)
 582 {
 583   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
 584   if (g_simple_async_result_propagate_error (simple, error))
 585     return NULL;
 586   return g_simple_async_result_get_op_res_gpointer (simple);
 587 }
 588 
 589 static CoglHandle
 590 data_to_cogl_handle (const guchar *data,
 591                      gboolean      has_alpha,
 592                      int           width,
 593                      int           height,
 594                      int           rowstride,
 595                      gboolean      add_padding)
 596 {
 597   CoglHandle texture, offscreen;
 598   CoglColor clear_color;
 599   guint size;
 600 
 601   size = MAX (width, height);
 602 
 603   if (!add_padding || width == height)
 604     return cogl_texture_new_from_data (width,
 605                                        height,
 606                                        COGL_TEXTURE_NONE,
 607                                        has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
 608                                        COGL_PIXEL_FORMAT_ANY,
 609                                        rowstride,
 610                                        data);
 611 
 612   texture = cogl_texture_new_with_size (size, size,
 613                                         COGL_TEXTURE_NO_SLICING,
 614                                         COGL_PIXEL_FORMAT_ANY);
 615 
 616   offscreen = cogl_offscreen_new_to_texture (texture);
 617   cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0);
 618   cogl_push_framebuffer (offscreen);
 619   cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
 620   cogl_pop_framebuffer ();
 621   cogl_handle_unref (offscreen);
 622 
 623   cogl_texture_set_region (texture,
 624                            0, 0,
 625                            (size - width) / 2, (size - height) / 2,
 626                            width, height,
 627                            width, height,
 628                            has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
 629                            rowstride,
 630                            data);
 631   return texture;
 632 }
 633 
 634 static CoglHandle
 635 pixbuf_to_cogl_handle (GdkPixbuf *pixbuf,
 636                        gboolean   add_padding)
 637 {
 638   return data_to_cogl_handle (gdk_pixbuf_get_pixels (pixbuf),
 639                               gdk_pixbuf_get_has_alpha (pixbuf),
 640                               gdk_pixbuf_get_width (pixbuf),
 641                               gdk_pixbuf_get_height (pixbuf),
 642                               gdk_pixbuf_get_rowstride (pixbuf),
 643                               add_padding);
 644 }
 645 
 646 static cairo_surface_t *
 647 pixbuf_to_cairo_surface (GdkPixbuf *pixbuf)
 648 {
 649   cairo_surface_t *dummy_surface;
 650   cairo_pattern_t *pattern;
 651   cairo_surface_t *surface;
 652   cairo_t *cr;
 653 
 654   dummy_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
 655 
 656   cr = cairo_create (dummy_surface);
 657   gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
 658   pattern = cairo_get_source (cr);
 659   cairo_pattern_get_surface (pattern, &surface);
 660   cairo_surface_reference (surface);
 661   cairo_destroy (cr);
 662   cairo_surface_destroy (dummy_surface);
 663 
 664   return surface;
 665 }
 666 
 667 static void
 668 on_pixbuf_loaded (GObject      *source,
 669                   GAsyncResult *result,
 670                   gpointer      user_data)
 671 {
 672   GSList *iter;
 673   StTextureCache *cache;
 674   AsyncTextureLoadData *data;
 675   GdkPixbuf *pixbuf;
 676   GError *error = NULL;
 677   CoglHandle texdata = NULL;
 678 
 679   data = user_data;
 680   cache = ST_TEXTURE_CACHE (source);
 681 
 682   g_hash_table_remove (cache->priv->outstanding_requests, data->key);
 683 
 684   pixbuf = load_pixbuf_async_finish (cache, result, &error);
 685   if (pixbuf == NULL)
 686     goto out;
 687 
 688   texdata = pixbuf_to_cogl_handle (pixbuf, data->enforced_square);
 689 
 690   g_object_unref (pixbuf);
 691 
 692   if (data->policy != ST_TEXTURE_CACHE_POLICY_NONE)
 693     {
 694       gpointer orig_key, value;
 695 
 696       if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key,
 697                                          &orig_key, &value))
 698         {
 699           cogl_handle_ref (texdata);
 700           g_hash_table_insert (cache->priv->keyed_cache, g_strdup (data->key),
 701                                texdata);
 702         }
 703     }
 704 
 705   for (iter = data->textures; iter; iter = iter->next)
 706     {
 707       ClutterTexture *texture = iter->data;
 708       set_texture_cogl_texture (texture, texdata);
 709     }
 710 
 711 out:
 712   if (texdata)
 713     cogl_handle_unref (texdata);
 714 
 715   texture_load_data_destroy (data);
 716   g_free (data);
 717 
 718   g_clear_error (&error);
 719 }
 720 
 721 static void
 722 load_texture_async (StTextureCache       *cache,
 723                     AsyncTextureLoadData *data)
 724 {
 725   GSimpleAsyncResult *result;
 726   result = g_simple_async_result_new (G_OBJECT (cache), on_pixbuf_loaded, data, load_texture_async);
 727   g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, NULL);
 728   g_object_unref (result);
 729 }
 730 
 731 typedef struct {
 732   StTextureCache *cache;
 733   ClutterTexture *texture;
 734   GObject *source;
 735   guint notify_signal_id;
 736   gboolean weakref_active;
 737 } StTextureCachePropertyBind;
 738 
 739 static void
 740 st_texture_cache_reset_texture (StTextureCachePropertyBind *bind,
 741                                 const char                 *propname)
 742 {
 743   GdkPixbuf *pixbuf;
 744   CoglHandle texdata;
 745 
 746   g_object_get (bind->source, propname, &pixbuf, NULL);
 747 
 748   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
 749 
 750   if (pixbuf != NULL)
 751     {
 752       texdata = pixbuf_to_cogl_handle (pixbuf, FALSE);
 753       g_object_unref (pixbuf);
 754 
 755       clutter_texture_set_cogl_texture (bind->texture, texdata);
'clutter_texture_set_cogl_texture' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:80)
(emitted by gcc)
756 cogl_handle_unref (texdata); 757 758 clutter_actor_set_opacity (CLUTTER_ACTOR (bind->texture), 255); 759 } 760 else 761 clutter_actor_set_opacity (CLUTTER_ACTOR (bind->texture), 0); 762 } 763 764 static void 765 st_texture_cache_on_pixbuf_notify (GObject *object, 766 GParamSpec *paramspec, 767 gpointer data) 768 { 769 StTextureCachePropertyBind *bind = data; 770 st_texture_cache_reset_texture (bind, paramspec->name); 771 } 772 773 static void 774 st_texture_cache_bind_weak_notify (gpointer data, 775 GObject *source_location) 776 { 777 StTextureCachePropertyBind *bind = data; 778 bind->weakref_active = FALSE; 779 g_signal_handler_disconnect (bind->source, bind->notify_signal_id); 780 } 781 782 static void 783 st_texture_cache_free_bind (gpointer data) 784 { 785 StTextureCachePropertyBind *bind = data; 786 if (bind->weakref_active) 787 g_object_weak_unref (G_OBJECT(bind->texture), st_texture_cache_bind_weak_notify, bind); 788 g_free (bind); 789 } 790 791 /** 792 * st_texture_cache_bind_pixbuf_property: 793 * @cache: 794 * @object: A #GObject with a property @property_name of type #GdkPixbuf 795 * @property_name: Name of a property 796 * 797 * Create a #ClutterTexture which tracks the #GdkPixbuf value of a GObject property 798 * named by @property_name. Unlike other methods in StTextureCache, the underlying 799 * CoglHandle is not shared by default with other invocations to this method. 800 * 801 * If the source object is destroyed, the texture will continue to show the last 802 * value of the property. 803 * 804 * Return value: (transfer none): A new #ClutterActor 805 */ 806 ClutterActor * 807 st_texture_cache_bind_pixbuf_property (StTextureCache *cache, 808 GObject *object, 809 const char *property_name) 810 { 811 ClutterTexture *texture; 812 gchar *notify_key; 813 StTextureCachePropertyBind *bind; 814 815 texture = CLUTTER_TEXTURE (clutter_texture_new ());
'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
(emitted by gcc)
816 817 bind = g_new0 (StTextureCachePropertyBind, 1); 818 bind->cache = cache; 819 bind->texture = texture; 820 bind->source = object; 821 g_object_weak_ref (G_OBJECT (texture), st_texture_cache_bind_weak_notify, bind); 822 bind->weakref_active = TRUE; 823 824 st_texture_cache_reset_texture (bind, property_name); 825 826 notify_key = g_strdup_printf ("notify::%s", property_name); 827 bind->notify_signal_id = g_signal_connect_data (object, notify_key, G_CALLBACK(st_texture_cache_on_pixbuf_notify), 828 bind, (GClosureNotify)st_texture_cache_free_bind, 0); 829 g_free (notify_key); 830 831 return CLUTTER_ACTOR(texture); 832 } 833 834 /** 835 * st_texture_cache_load: (skip) 836 * @cache: A #StTextureCache 837 * @key: Arbitrary string used to refer to item 838 * @policy: Caching policy 839 * @load: Function to create the texture, if not already cached 840 * @data: User data passed to @load 841 * @error: A #GError 842 * 843 * Load an arbitrary texture, caching it. The string chosen for @key 844 * should be of the form "type-prefix:type-uuid". For example, 845 * "url:file:///usr/share/icons/hicolor/48x48/apps/firefox.png", or 846 * "stock-icon:gtk-ok". 847 * 848 * Returns: (transfer full): A newly-referenced handle to the texture 849 */ 850 CoglHandle 851 st_texture_cache_load (StTextureCache *cache, 852 const char *key, 853 StTextureCachePolicy policy, 854 StTextureCacheLoader load, 855 void *data, 856 GError **error) 857 { 858 CoglHandle texture; 859 860 texture = g_hash_table_lookup (cache->priv->keyed_cache, key); 861 if (!texture) 862 { 863 texture = load (cache, key, data, error); 864 if (texture) 865 g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texture); 866 else 867 return COGL_INVALID_HANDLE; 868 } 869 cogl_handle_ref (texture); 870 return texture; 871 } 872 873 /** 874 * ensure_request: 875 * @cache: 876 * @key: A cache key 877 * @policy: Cache policy 878 * @request: (out): If no request is outstanding, one will be created and returned here 879 * @texture: A texture to be added to the request 880 * 881 * Check for any outstanding load for the data represented by @key. If there 882 * is already a request pending, append it to that request to avoid loading 883 * the data multiple times. 884 * 885 * Returns: %TRUE iff there is already a request pending 886 */ 887 static gboolean 888 ensure_request (StTextureCache *cache, 889 const char *key, 890 StTextureCachePolicy policy, 891 AsyncTextureLoadData **request, 892 ClutterActor *texture) 893 { 894 CoglHandle texdata; 895 AsyncTextureLoadData *pending; 896 gboolean had_pending; 897 898 texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); 899 900 if (texdata != NULL) 901 { 902 /* We had this cached already, just set the texture and we're done. */ 903 set_texture_cogl_texture (CLUTTER_TEXTURE (texture), texdata); 904 return TRUE; 905 } 906 907 pending = g_hash_table_lookup (cache->priv->outstanding_requests, key); 908 had_pending = pending != NULL; 909 910 if (pending == NULL) 911 { 912 /* Not cached and no pending request, create it */ 913 *request = g_new0 (AsyncTextureLoadData, 1); 914 if (policy != ST_TEXTURE_CACHE_POLICY_NONE) 915 g_hash_table_insert (cache->priv->outstanding_requests, g_strdup (key), *request); 916 } 917 else 918 *request = pending; 919 920 /* Regardless of whether there was a pending request, prepend our texture here. */ 921 (*request)->textures = g_slist_prepend ((*request)->textures, g_object_ref (texture)); 922 923 return had_pending; 924 } 925 926 static ClutterActor * 927 load_gicon_with_colors (StTextureCache *cache, 928 GIcon *icon, 929 gint size, 930 StIconColors *colors) 931 { 932 AsyncTextureLoadData *request; 933 ClutterActor *texture; 934 char *gicon_string; 935 char *key; 936 GtkIconTheme *theme; 937 GtkIconInfo *info; 938 StTextureCachePolicy policy; 939 940 /* Do theme lookups in the main thread to avoid thread-unsafety */ 941 theme = cache->priv->icon_theme; 942 943 info = gtk_icon_theme_lookup_by_gicon (theme, icon, size, GTK_ICON_LOOKUP_USE_BUILTIN); 944 if (info == NULL) 945 return NULL; 946 947 gicon_string = g_icon_to_string (icon); 948 /* A return value of NULL indicates that the icon can not be serialized, 949 * so don't have a unique identifier for it as a cache key, and thus can't 950 * be cached. If it is cachable, we hardcode a policy of FOREVER here for 951 * now; we should actually blow this away on icon theme changes probably */ 952 policy = gicon_string != NULL ? ST_TEXTURE_CACHE_POLICY_FOREVER 953 : ST_TEXTURE_CACHE_POLICY_NONE; 954 if (colors) 955 { 956 /* This raises some doubts about the practice of using string keys */ 957 key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d,colors=%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x,%2x%2x%2x%2x", 958 gicon_string, size, 959 colors->foreground.red, colors->foreground.blue, colors->foreground.green, colors->foreground.alpha, 960 colors->warning.red, colors->warning.blue, colors->warning.green, colors->warning.alpha, 961 colors->error.red, colors->error.blue, colors->error.green, colors->error.alpha, 962 colors->success.red, colors->success.blue, colors->success.green, colors->success.alpha); 963 } 964 else 965 { 966 key = g_strdup_printf (CACHE_PREFIX_ICON "%s,size=%d", 967 gicon_string, size); 968 } 969 g_free (gicon_string); 970 971 texture = (ClutterActor *) create_default_texture (); 972 clutter_actor_set_size (texture, size, size); 973 974 if (ensure_request (cache, key, policy, &request, texture)) 975 { 976 /* If there's an outstanding request, we've just added ourselves to it */ 977 gtk_icon_info_free (info); 978 g_free (key); 979 } 980 else 981 { 982 /* Else, make a new request */ 983 984 request->cache = cache; 985 /* Transfer ownership of key */ 986 request->key = key; 987 request->policy = policy; 988 request->colors = colors ? st_icon_colors_ref (colors) : NULL; 989 request->icon_info = info; 990 request->width = request->height = size; 991 request->enforced_square = TRUE; 992 993 load_texture_async (cache, request); 994 } 995 996 return CLUTTER_ACTOR (texture); 997 } 998 999 /** 1000 * st_texture_cache_load_gicon: 1001 * @cache: The texture cache instance 1002 * @theme_node: (allow-none): The #StThemeNode to use for colors, or NULL 1003 * if the icon must not be recolored 1004 * @icon: the #GIcon to load 1005 * @size: Size of themed 1006 * 1007 * This method returns a new #ClutterActor for a given #GIcon. If the 1008 * icon isn't loaded already, the texture will be filled 1009 * asynchronously. 1010 * 1011 * Return Value: (transfer none): A new #ClutterActor for the icon, or %NULL if not found 1012 */ 1013 ClutterActor * 1014 st_texture_cache_load_gicon (StTextureCache *cache, 1015 StThemeNode *theme_node, 1016 GIcon *icon, 1017 gint size) 1018 { 1019 return load_gicon_with_colors (cache, icon, size, theme_node ? st_theme_node_get_icon_colors (theme_node) : NULL); 1020 } 1021 1022 static ClutterActor * 1023 load_from_pixbuf (GdkPixbuf *pixbuf) 1024 { 1025 ClutterTexture *texture; 1026 CoglHandle texdata; 1027 int width = gdk_pixbuf_get_width (pixbuf); 1028 int height = gdk_pixbuf_get_height (pixbuf); 1029 1030 texture = create_default_texture (); 1031 1032 clutter_actor_set_size (CLUTTER_ACTOR (texture), width, height); 1033 1034 texdata = pixbuf_to_cogl_handle (pixbuf, FALSE); 1035 1036 set_texture_cogl_texture (texture, texdata); 1037 1038 cogl_handle_unref (texdata); 1039 return CLUTTER_ACTOR (texture); 1040 } 1041 1042 typedef struct { 1043 gchar *path; 1044 gint grid_width, grid_height; 1045 ClutterActor *actor; 1046 } AsyncImageData; 1047 1048 static void 1049 on_data_destroy (gpointer data) 1050 { 1051 AsyncImageData *d = (AsyncImageData *)data; 1052 g_free (d->path); 1053 g_object_unref (d->actor); 1054 g_free (d); 1055 } 1056 1057 static void 1058 on_sliced_image_loaded (GObject *source_object, 1059 GAsyncResult *res, 1060 gpointer user_data) 1061 { 1062 AsyncImageData *data = (AsyncImageData *)user_data; 1063 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); 1064 GList *list; 1065 1066 if (g_simple_async_result_propagate_error (simple, NULL)) 1067 return; 1068 1069 for (list = g_simple_async_result_get_op_res_gpointer (simple); list; list = g_list_next (list)) 1070 { 1071 ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data)); 1072 clutter_actor_hide (actor); 1073 clutter_actor_add_child (data->actor, actor); 1074 } 1075 } 1076 1077 static void 1078 free_glist_unref_gobjects (gpointer p) 1079 { 1080 GList *list = p; 1081 GList *iter; 1082 1083 for (iter = list; iter; iter = iter->next) 1084 g_object_unref (iter->data); 1085 g_list_free (list); 1086 } 1087 1088 static void 1089 load_sliced_image (GSimpleAsyncResult *result, 1090 GObject *object, 1091 GCancellable *cancellable) 1092 { 1093 AsyncImageData *data; 1094 GList *res = NULL; 1095 GdkPixbuf *pix; 1096 gint width, height, y, x; 1097 1098 g_assert (!cancellable); 1099 1100 data = g_object_get_data (G_OBJECT (result), "load_sliced_image"); 1101 g_assert (data); 1102 1103 if (!(pix = gdk_pixbuf_new_from_file (data->path, NULL))) 1104 return; 1105 1106 width = gdk_pixbuf_get_width (pix); 1107 height = gdk_pixbuf_get_height (pix); 1108 for (y = 0; y < height; y += data->grid_height) 1109 { 1110 for (x = 0; x < width; x += data->grid_width) 1111 { 1112 GdkPixbuf *pixbuf = gdk_pixbuf_new_subpixbuf (pix, x, y, data->grid_width, data->grid_height); 1113 g_assert (pixbuf != NULL); 1114 res = g_list_append (res, pixbuf); 1115 } 1116 } 1117 /* We don't need the original pixbuf anymore, though the subpixbufs 1118 will hold a reference. */ 1119 g_object_unref (pix); 1120 g_simple_async_result_set_op_res_gpointer (result, res, free_glist_unref_gobjects); 1121 } 1122 1123 /** 1124 * st_texture_cache_load_sliced_image: 1125 * @cache: A #StTextureCache 1126 * @path: Path to a filename 1127 * @grid_width: Width in pixels 1128 * @grid_height: Height in pixels 1129 * 1130 * This function reads a single image file which contains multiple images internally. 1131 * The image file will be divided using @grid_width and @grid_height; 1132 * note that the dimensions of the image loaded from @path 1133 * should be a multiple of the specified grid dimensions. 1134 * 1135 * Returns: (transfer none): A new #ClutterActor 1136 */ 1137 ClutterActor * 1138 st_texture_cache_load_sliced_image (StTextureCache *cache, 1139 const gchar *path, 1140 gint grid_width, 1141 gint grid_height) 1142 { 1143 AsyncImageData *data; 1144 GSimpleAsyncResult *result; 1145 ClutterActor *actor = clutter_actor_new (); 1146 1147 data = g_new0 (AsyncImageData, 1); 1148 data->grid_width = grid_width; 1149 data->grid_height = grid_height; 1150 data->path = g_strdup (path); 1151 data->actor = actor; 1152 g_object_ref (G_OBJECT (actor)); 1153 1154 result = g_simple_async_result_new (G_OBJECT (cache), on_sliced_image_loaded, data, st_texture_cache_load_sliced_image); 1155 1156 g_object_set_data_full (G_OBJECT (result), "load_sliced_image", data, on_data_destroy); 1157 g_simple_async_result_run_in_thread (result, load_sliced_image, G_PRIORITY_DEFAULT, NULL); 1158 1159 g_object_unref (result); 1160 1161 return actor; 1162 } 1163 1164 /** 1165 * st_texture_cache_load_icon_name: 1166 * @cache: The texture cache instance 1167 * @theme_node: (allow-none): a #StThemeNode 1168 * @name: Name of a themed icon 1169 * @size: Size of themed icon 1170 * 1171 * Load a themed icon into a texture. The colors used for symbolic 1172 * icons are derived from @theme_node. 1173 * 1174 * Return Value: (transfer none): A new #ClutterTexture for the icon 1175 */ 1176 ClutterActor * 1177 st_texture_cache_load_icon_name (StTextureCache *cache, 1178 StThemeNode *theme_node, 1179 const char *name, 1180 gint size) 1181 { 1182 ClutterActor *texture; 1183 GIcon *themed; 1184 1185 themed = g_themed_icon_new_with_default_fallbacks (name); 1186 texture = load_gicon_with_colors (cache, themed, size, 1187 theme_node ? st_theme_node_get_icon_colors (theme_node) : NULL); 1188 g_object_unref (themed); 1189 1190 if (texture == NULL) 1191 { 1192 texture = (ClutterActor *) create_default_texture (); 1193 clutter_actor_set_size (texture, size, size); 1194 } 1195 1196 return texture; 1197 } 1198 1199 /** 1200 * st_texture_cache_load_uri_async: 1201 * @cache: The texture cache instance 1202 * @uri: uri of the image file from which to create a pixbuf 1203 * @available_width: available width for the image, can be -1 if not limited 1204 * @available_height: available height for the image, can be -1 if not limited 1205 * 1206 * Asynchronously load an image. Initially, the returned texture will have a natural 1207 * size of zero. At some later point, either the image will be loaded successfully 1208 * and at that point size will be negotiated, or upon an error, no image will be set. 1209 * 1210 * Return value: (transfer none): A new #ClutterActor with no image loaded initially. 1211 */ 1212 ClutterActor * 1213 st_texture_cache_load_uri_async (StTextureCache *cache, 1214 const gchar *uri, 1215 int available_width, 1216 int available_height) 1217 { 1218 ClutterActor *texture; 1219 AsyncTextureLoadData *request; 1220 StTextureCachePolicy policy; 1221 gchar *key; 1222 1223 key = g_strconcat (CACHE_PREFIX_URI, uri, NULL); 1224 1225 policy = ST_TEXTURE_CACHE_POLICY_NONE; /* XXX */ 1226 1227 texture = (ClutterActor *) create_default_texture (); 1228 1229 if (ensure_request (cache, key, policy, &request, texture)) 1230 { 1231 /* If there's an outstanding request, we've just added ourselves to it */ 1232 g_free (key); 1233 } 1234 else 1235 { 1236 /* Else, make a new request */ 1237 1238 request->cache = cache; 1239 /* Transfer ownership of key */ 1240 request->key = key; 1241 request->uri = g_strdup (uri); 1242 request->policy = policy; 1243 request->width = available_width; 1244 request->height = available_height; 1245 1246 load_texture_async (cache, request); 1247 } 1248 1249 return CLUTTER_ACTOR (texture); 1250 } 1251 1252 static CoglHandle 1253 st_texture_cache_load_uri_sync_to_cogl_texture (StTextureCache *cache, 1254 StTextureCachePolicy policy, 1255 const gchar *uri, 1256 int available_width, 1257 int available_height, 1258 GError **error) 1259 { 1260 CoglHandle texdata; 1261 GdkPixbuf *pixbuf; 1262 char *key; 1263 1264 key = g_strconcat (CACHE_PREFIX_URI, uri, NULL); 1265 1266 texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); 1267 1268 if (texdata == NULL) 1269 { 1270 pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error); 1271 if (!pixbuf) 1272 goto out; 1273 1274 texdata = pixbuf_to_cogl_handle (pixbuf, FALSE); 1275 g_object_unref (pixbuf); 1276 1277 if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER) 1278 { 1279 cogl_handle_ref (texdata); 1280 g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texdata); 1281 } 1282 } 1283 else 1284 cogl_handle_ref (texdata); 1285 1286 out: 1287 g_free (key); 1288 return texdata; 1289 } 1290 1291 static cairo_surface_t * 1292 st_texture_cache_load_uri_sync_to_cairo_surface (StTextureCache *cache, 1293 StTextureCachePolicy policy, 1294 const gchar *uri, 1295 int available_width, 1296 int available_height, 1297 GError **error) 1298 { 1299 cairo_surface_t *surface; 1300 GdkPixbuf *pixbuf; 1301 char *key; 1302 1303 key = g_strconcat (CACHE_PREFIX_URI_FOR_CAIRO, uri, NULL); 1304 1305 surface = g_hash_table_lookup (cache->priv->keyed_cache, key); 1306 1307 if (surface == NULL) 1308 { 1309 pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error); 1310 if (!pixbuf) 1311 goto out; 1312 1313 surface = pixbuf_to_cairo_surface (pixbuf); 1314 g_object_unref (pixbuf); 1315 1316 if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER) 1317 { 1318 cairo_surface_reference (surface); 1319 g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), surface); 1320 } 1321 } 1322 else 1323 cairo_surface_reference (surface); 1324 1325 out: 1326 g_free (key); 1327 return surface; 1328 } 1329 1330 /** 1331 * st_texture_cache_load_file_to_cogl_texture: 1332 * @cache: A #StTextureCache 1333 * @file_path: Path to a file in supported image format 1334 * 1335 * This function synchronously loads the given file path 1336 * into a COGL texture. On error, a warning is emitted 1337 * and %COGL_INVALID_HANDLE is returned. 1338 * 1339 * Returns: (transfer full): a new #CoglHandle 1340 */ 1341 CoglHandle 1342 st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, 1343 const gchar *file_path) 1344 { 1345 CoglHandle texture; 1346 GFile *file; 1347 char *uri; 1348 GError *error = NULL; 1349 1350 file = g_file_new_for_path (file_path); 1351 uri = g_file_get_uri (file); 1352 1353 texture = st_texture_cache_load_uri_sync_to_cogl_texture (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, 1354 uri, -1, -1, &error); 1355 g_object_unref (file); 1356 g_free (uri); 1357 1358 if (texture == NULL) 1359 { 1360 g_warning ("Failed to load %s: %s", file_path, error->message);
Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1361 g_clear_error (&error); 1362 return COGL_INVALID_HANDLE; 1363 } 1364 return texture; 1365 } 1366 1367 /** 1368 * st_texture_cache_load_file_to_cairo_surface: 1369 * @cache: A #StTextureCache 1370 * @file_path: Path to a file in supported image format 1371 * 1372 * This function synchronously loads the given file path 1373 * into a cairo surface. On error, a warning is emitted 1374 * and %NULL is returned. 1375 * 1376 * Returns: (transfer full): a new #cairo_surface_t 1377 */ 1378 cairo_surface_t * 1379 st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache, 1380 const gchar *file_path) 1381 { 1382 cairo_surface_t *surface; 1383 GFile *file; 1384 char *uri; 1385 GError *error = NULL; 1386 1387 file = g_file_new_for_path (file_path); 1388 uri = g_file_get_uri (file); 1389 1390 surface = st_texture_cache_load_uri_sync_to_cairo_surface (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, 1391 uri, -1, -1, &error); 1392 g_object_unref (file); 1393 g_free (uri); 1394 1395 if (surface == NULL) 1396 { 1397 g_warning ("Failed to load %s: %s", file_path, error->message);
Access to field 'message' results in a dereference of a null pointer (loaded from variable 'error')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1398 g_clear_error (&error); 1399 return NULL; 1400 } 1401 return surface; 1402 } 1403 1404 /** 1405 * st_texture_cache_load_from_raw: 1406 * @cache: a #StTextureCache 1407 * @data: (array length=len): raw pixel data 1408 * @len: the length of @data 1409 * @has_alpha: whether @data includes an alpha channel 1410 * @width: width in pixels of @data 1411 * @height: width in pixels of @data 1412 * @rowstride: rowstride of @data 1413 * @size: size of icon to return 1414 * 1415 * Creates (or retrieves from cache) an icon based on raw pixel data. 1416 * 1417 * Return value: (transfer none): a new #ClutterActor displaying a 1418 * pixbuf created from @data and the other parameters. 1419 **/ 1420 ClutterActor * 1421 st_texture_cache_load_from_raw (StTextureCache *cache, 1422 const guchar *data, 1423 gsize len, 1424 gboolean has_alpha, 1425 int width, 1426 int height, 1427 int rowstride, 1428 int size, 1429 GError **error) 1430 { 1431 ClutterTexture *texture; 1432 CoglHandle texdata; 1433 char *key; 1434 char *checksum; 1435 1436 texture = create_default_texture (); 1437 clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size); 1438 1439 /* In theory, two images of with different width and height could have the same 1440 * pixel data and thus hash the same. (Say, a 16x16 and a 8x32 blank image.) 1441 * We ignore this for now. If anybody hits this problem they should use 1442 * GChecksum directly to compute a checksum including the width and height. 1443 */ 1444 checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len); 1445 key = g_strdup_printf (CACHE_PREFIX_RAW_CHECKSUM "checksum=%s", checksum); 1446 g_free (checksum); 1447 1448 texdata = g_hash_table_lookup (cache->priv->keyed_cache, key); 1449 if (texdata == NULL) 1450 { 1451 texdata = data_to_cogl_handle (data, has_alpha, width, height, rowstride, TRUE); 1452 g_hash_table_insert (cache->priv->keyed_cache, g_strdup (key), texdata); 1453 } 1454 1455 g_free (key); 1456 1457 set_texture_cogl_texture (texture, texdata); 1458 return CLUTTER_ACTOR (texture); 1459 } 1460 1461 static StTextureCache *instance = NULL; 1462 1463 /** 1464 * st_texture_cache_get_default: 1465 * 1466 * Return value: (transfer none): The global texture cache 1467 */ 1468 StTextureCache* 1469 st_texture_cache_get_default (void) 1470 { 1471 if (instance == NULL) 1472 instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL); 1473 return instance; 1474 }