~ubuntu-branches/ubuntu/trusty/gnome-shell/trusty-proposed

« back to all changes in this revision

Viewing changes to src/st/st-texture-cache.c

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2012-03-14 13:47:20 UTC
  • mfrom: (1.1.36) (18.1.8 sid)
  • Revision ID: package-import@ubuntu.com-20120314134720-202sbjbu4a3z1fru
Tags: 3.3.90-0ubuntu1
* Sync with Debian experimental svn packaging (LP: #941755, #937709).
  Remaining changes:
  - debian/gnome-shell.gsettings-override: Update for Ubuntu defaults
  - debian/control.in: Recommend cups-pk-helper
  - debian/patches/10-make-NetworkManager-optional.patch: Disabled
  - Don't run dh-autoreconf

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
 
24
24
#include "st-texture-cache.h"
25
25
#include <gtk/gtk.h>
26
 
#define GNOME_DESKTOP_USE_UNSTABLE_API
27
 
#include <libgnome-desktop/gnome-desktop-thumbnail.h>
28
26
#include <string.h>
29
27
#include <glib.h>
30
28
 
31
29
#define CACHE_PREFIX_GICON "gicon:"
32
30
#define CACHE_PREFIX_URI "uri:"
33
31
#define CACHE_PREFIX_URI_FOR_CAIRO "uri-for-cairo:"
34
 
#define CACHE_PREFIX_THUMBNAIL_URI "thumbnail-uri:"
35
32
#define CACHE_PREFIX_RAW_CHECKSUM "raw-checksum:"
36
33
#define CACHE_PREFIX_COMPRESSED_CHECKSUM "compressed-checksum:"
37
34
 
46
43
   * cases too.
47
44
   */
48
45
  GHashTable *outstanding_requests; /* char * -> AsyncTextureLoadData * */
49
 
  GnomeDesktopThumbnailFactory *thumbnails;
50
46
};
51
47
 
52
48
static void st_texture_cache_dispose (GObject *object);
95
91
                  G_TYPE_FROM_CLASS (klass),
96
92
                  G_SIGNAL_RUN_LAST,
97
93
                  0, /* no default handler slot */
98
 
                  NULL, NULL,
99
 
                  g_cclosure_marshal_VOID__VOID,
 
94
                  NULL, NULL, NULL,
100
95
                  G_TYPE_NONE, 0);
101
96
}
102
97
 
143
138
                                                   g_free, cogl_handle_unref);
144
139
  self->priv->outstanding_requests = g_hash_table_new_full (g_str_hash, g_str_equal,
145
140
                                                            g_free, NULL);
146
 
  self->priv->thumbnails = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE);
147
141
}
148
142
 
149
143
static void
167
161
    g_hash_table_destroy (self->priv->outstanding_requests);
168
162
  self->priv->outstanding_requests = NULL;
169
163
 
170
 
  if (self->priv->thumbnails)
171
 
    g_object_unref (self->priv->thumbnails);
172
 
  self->priv->thumbnails = NULL;
173
 
 
174
164
  G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
175
165
}
176
166
 
180
170
  G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
181
171
}
182
172
 
183
 
typedef struct {
184
 
  StTextureCache *cache;
185
 
  char *uri;
186
 
  char *mimetype;
187
 
  gboolean thumbnail;
188
 
  GIcon *icon;
189
 
  GtkRecentInfo *recent_info;
190
 
  GtkIconInfo *icon_info;
191
 
  gint width;
192
 
  gint height;
193
 
  StIconColors *colors;
194
 
  gpointer user_data;
195
 
} AsyncIconLookupData;
196
 
 
197
173
static gboolean
198
174
compute_pixbuf_scale (gint      width,
199
175
                      gint      height,
209
185
 
210
186
  if (available_width >= 0 && available_height >= 0)
211
187
    {
212
 
      // This should keep the aspect ratio of the image intact, because if
213
 
      // available_width < (available_height * width) / height
214
 
      // than
215
 
      // (available_width * height) / width < available_height
216
 
      // So we are guaranteed to either scale the image to have an available_width
217
 
      // for width and height scaled accordingly OR have the available_height
218
 
      // for height and width scaled accordingly, whichever scaling results
219
 
      // in the image that can fit both available dimensions.
 
188
      /* This should keep the aspect ratio of the image intact, because if
 
189
       * available_width < (available_height * width) / height
 
190
       * then
 
191
       * (available_width * height) / width < available_height
 
192
       * So we are guaranteed to either scale the image to have an available_width
 
193
       * for width and height scaled accordingly OR have the available_height
 
194
       * for height and width scaled accordingly, whichever scaling results
 
195
       * in the image that can fit both available dimensions.
 
196
       */
220
197
      scaled_width = MIN (available_width, (available_height * width) / height);
221
198
      scaled_height = MIN (available_height, (available_width * height) / width);
222
199
    }
235
212
      scaled_width = scaled_height = 0;
236
213
    }
237
214
 
238
 
  // Scale the image only if that will not increase its original dimensions.
 
215
  /* Scale the image only if that will not increase its original dimensions. */
239
216
  if (scaled_width > 0 && scaled_height > 0 && scaled_width < width && scaled_height < height)
240
217
    {
241
218
      *new_width = scaled_width;
306
283
  return pixbuf;
307
284
}
308
285
 
309
 
// A private structure for keeping width and height.
 
286
/* A private structure for keeping width and height. */
310
287
typedef struct {
311
288
  int width;
312
289
  int height;
313
290
} Dimensions;
314
291
 
 
292
/* This struct corresponds to a request for an texture.
 
293
 * It's creasted when something needs a new texture,
 
294
 * and destroyed when the texture data is loaded. */
 
295
typedef struct {
 
296
  StTextureCache *cache;
 
297
  StTextureCachePolicy policy;
 
298
  char *key;
 
299
  char *checksum;
 
300
 
 
301
  gboolean enforced_square;
 
302
 
 
303
  guint width;
 
304
  guint height;
 
305
  GSList *textures;
 
306
 
 
307
  GIcon *icon;
 
308
  char *mimetype;
 
309
  GtkIconInfo *icon_info;
 
310
  StIconColors *colors;
 
311
  GtkRecentInfo *recent_info;
 
312
  char *uri;
 
313
} AsyncTextureLoadData;
 
314
 
315
315
static void
316
 
icon_lookup_data_destroy (gpointer p)
 
316
texture_load_data_destroy (gpointer p)
317
317
{
318
 
  AsyncIconLookupData *data = p;
 
318
  AsyncTextureLoadData *data = p;
319
319
 
320
320
  if (data->icon)
321
321
    {
322
322
      g_object_unref (data->icon);
323
323
      gtk_icon_info_free (data->icon_info);
 
324
      if (data->colors)
 
325
        st_icon_colors_unref (data->colors);
324
326
    }
325
327
  else if (data->uri)
326
328
    g_free (data->uri);
 
329
  else if (data->recent_info)
 
330
    gtk_recent_info_unref (data->recent_info);
 
331
 
 
332
  if (data->key)
 
333
    g_free (data->key);
 
334
  if (data->checksum)
 
335
    g_free (data->checksum);
327
336
  if (data->mimetype)
328
337
    g_free (data->mimetype);
329
 
  if (data->recent_info)
330
 
    gtk_recent_info_unref (data->recent_info);
331
 
  if (data->colors)
332
 
    st_icon_colors_unref (data->colors);
333
338
 
334
 
  g_free (data);
 
339
  if (data->textures)
 
340
    g_slist_free_full (data->textures, (GDestroyNotify) g_object_unref);
335
341
}
336
342
 
337
343
/**
401
407
  rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
402
408
  width_after_rotation = gdk_pixbuf_get_width (rotated_pixbuf);
403
409
 
404
 
  // There is currently no way to tell if the pixbuf will need to be rotated before it is loaded,
405
 
  // so we only check that once it is loaded, and reload it again if it needs to be rotated in order
406
 
  // to use the available width and height correctly.
407
 
  // http://bugzilla.gnome.org/show_bug.cgi?id=579003
 
410
  /* There is currently no way to tell if the pixbuf will need to be rotated before it is loaded,
 
411
   * so we only check that once it is loaded, and reload it again if it needs to be rotated in order
 
412
   * to use the available width and height correctly.
 
413
   * See http://bugzilla.gnome.org/show_bug.cgi?id=579003
 
414
   */
408
415
  if (width_before_rotation != width_after_rotation)
409
416
    {
410
417
      g_object_unref (pixbuf_loader);
413
420
 
414
421
      pixbuf_loader = gdk_pixbuf_loader_new ();
415
422
 
416
 
      // We know that the image will later be rotated, so we reverse the available dimensions.
 
423
      /* We know that the image will later be rotated, so we reverse the available dimensions. */
417
424
      available_dimensions.width = available_height;
418
425
      available_dimensions.height = available_width;
419
426
      g_signal_connect (pixbuf_loader, "size-prepared",
527
534
  return pixbuf;
528
535
}
529
536
 
530
 
static GdkPixbuf *
531
 
impl_load_thumbnail (StTextureCache    *cache,
532
 
                     const char        *uri,
533
 
                     const char        *mime_type,
534
 
                     guint              size,
535
 
                     GError           **error)
536
 
{
537
 
  GnomeDesktopThumbnailFactory *thumbnail_factory;
538
 
  GdkPixbuf *pixbuf = NULL;
539
 
  GFile *file;
540
 
  GFileInfo *file_info;
541
 
  GTimeVal mtime_g;
542
 
  time_t mtime = 0;
543
 
  char *existing_thumbnail;
544
 
 
545
 
  file = g_file_new_for_uri (uri);
546
 
  file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
547
 
  g_object_unref (file);
548
 
  if (file_info)
549
 
    {
550
 
      g_file_info_get_modification_time (file_info, &mtime_g);
551
 
      g_object_unref (file_info);
552
 
      mtime = (time_t) mtime_g.tv_sec;
553
 
    }
554
 
 
555
 
  thumbnail_factory = cache->priv->thumbnails;
556
 
 
557
 
  existing_thumbnail = gnome_desktop_thumbnail_factory_lookup (thumbnail_factory, uri, mtime);
558
 
 
559
 
  if (existing_thumbnail != NULL)
560
 
    {
561
 
      pixbuf = gdk_pixbuf_new_from_file_at_size (existing_thumbnail, size, size, error);
562
 
      g_free (existing_thumbnail);
563
 
    }
564
 
  else if (gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (thumbnail_factory, uri, mtime))
565
 
    g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Has failed thumbnail");
566
 
  else if (gnome_desktop_thumbnail_factory_can_thumbnail (thumbnail_factory, uri, mime_type, mtime))
567
 
    {
568
 
      pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type);
569
 
      if (pixbuf)
570
 
        {
571
 
          // we need to save the thumbnail so that we don't need to generate it again in the future
572
 
          gnome_desktop_thumbnail_factory_save_thumbnail (thumbnail_factory, pixbuf, uri, mtime);
573
 
        }
574
 
      else
575
 
        {
576
 
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to generate thumbnail");
577
 
          gnome_desktop_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, uri, mtime);
578
 
        }
579
 
     }
580
 
   return pixbuf;
581
 
}
582
 
 
583
537
static GIcon *
584
538
icon_for_mimetype (const char *mimetype)
585
539
{
601
555
                    GCancellable *cancellable)
602
556
{
603
557
  GdkPixbuf *pixbuf;
604
 
  AsyncIconLookupData *data;
 
558
  AsyncTextureLoadData *data;
605
559
  GError *error = NULL;
606
560
 
607
 
  data = g_object_get_data (G_OBJECT (result), "load_pixbuf_async");
 
561
  data = g_async_result_get_user_data (G_ASYNC_RESULT (result));
608
562
  g_assert (data != NULL);
609
563
 
610
 
  if (data->thumbnail)
611
 
    {
612
 
      const char *uri;
613
 
      const char *mimetype;
614
 
 
615
 
      if (data->recent_info)
616
 
        {
617
 
          uri = gtk_recent_info_get_uri (data->recent_info);
618
 
          mimetype = gtk_recent_info_get_mime_type (data->recent_info);
619
 
        }
620
 
      else
621
 
        {
622
 
          uri = data->uri;
623
 
          mimetype = data->mimetype;
624
 
        }
625
 
      pixbuf = impl_load_thumbnail (data->cache, uri, mimetype, data->width, &error);
626
 
    }
627
 
  else if (data->uri)
 
564
  if (data->uri)
628
565
    pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error);
629
566
  else if (data->icon)
630
567
    pixbuf = impl_load_pixbuf_gicon (data->icon, data->icon_info, data->width, data->colors, &error);
642
579
                                               g_object_unref);
643
580
}
644
581
 
645
 
/**
646
 
 * load_icon_pixbuf_async:
647
 
 *
648
 
 * Asynchronously load the #GdkPixbuf associated with a #GIcon.  Currently
649
 
 * the #GtkIconInfo must have already been provided.
650
 
 */
651
 
static void
652
 
load_icon_pixbuf_async (StTextureCache       *cache,
653
 
                        GIcon                *icon,
654
 
                        GtkIconInfo          *icon_info,
655
 
                        gint                  size,
656
 
                        StIconColors         *colors,
657
 
                        GCancellable         *cancellable,
658
 
                        GAsyncReadyCallback   callback,
659
 
                        gpointer              user_data)
660
 
{
661
 
  GSimpleAsyncResult *result;
662
 
  AsyncIconLookupData *data;
663
 
 
664
 
  data = g_new0 (AsyncIconLookupData, 1);
665
 
  data->cache = cache;
666
 
  data->icon = g_object_ref (icon);
667
 
  data->icon_info = gtk_icon_info_copy (icon_info);
668
 
  data->width = data->height = size;
669
 
  if (colors)
670
 
    data->colors = st_icon_colors_ref (colors);
671
 
  else
672
 
    data->colors = NULL;
673
 
  data->user_data = user_data;
674
 
 
675
 
  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_icon_pixbuf_async);
676
 
 
677
 
  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
678
 
  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
679
 
 
680
 
  g_object_unref (result);
681
 
}
682
 
 
683
 
static void
684
 
load_uri_pixbuf_async (StTextureCache     *cache,
685
 
                       const char         *uri,
686
 
                       guint               width,
687
 
                       guint               height,
688
 
                       GCancellable       *cancellable,
689
 
                       GAsyncReadyCallback callback,
690
 
                       gpointer            user_data)
691
 
{
692
 
  GSimpleAsyncResult *result;
693
 
  AsyncIconLookupData *data;
694
 
 
695
 
  data = g_new0 (AsyncIconLookupData, 1);
696
 
  data->cache = cache;
697
 
  data->uri = g_strdup (uri);
698
 
  data->width = width;
699
 
  data->height = height;
700
 
  data->user_data = user_data;
701
 
 
702
 
  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_uri_pixbuf_async);
703
 
 
704
 
  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
705
 
  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
706
 
 
707
 
  g_object_unref (result);
708
 
}
709
 
 
710
 
static void
711
 
load_thumbnail_async (StTextureCache     *cache,
712
 
                      const char         *uri,
713
 
                      const char         *mimetype,
714
 
                      guint               size,
715
 
                      GCancellable       *cancellable,
716
 
                      GAsyncReadyCallback callback,
717
 
                      gpointer            user_data)
718
 
{
719
 
  GSimpleAsyncResult *result;
720
 
  AsyncIconLookupData *data;
721
 
 
722
 
  data = g_new0 (AsyncIconLookupData, 1);
723
 
  data->cache = cache;
724
 
  data->uri = g_strdup (uri);
725
 
  data->mimetype = g_strdup (mimetype);
726
 
  data->thumbnail = TRUE;
727
 
  data->width = size;
728
 
  data->height = size;
729
 
  data->user_data = user_data;
730
 
 
731
 
  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_thumbnail_async);
732
 
 
733
 
  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
734
 
  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
735
 
 
736
 
  g_object_unref (result);
737
 
}
738
 
 
739
 
static void
740
 
load_recent_thumbnail_async (StTextureCache     *cache,
741
 
                             GtkRecentInfo      *info,
742
 
                             guint               size,
743
 
                             GCancellable       *cancellable,
744
 
                             GAsyncReadyCallback callback,
745
 
                             gpointer            user_data)
746
 
{
747
 
  GSimpleAsyncResult *result;
748
 
  AsyncIconLookupData *data;
749
 
 
750
 
  data = g_new0 (AsyncIconLookupData, 1);
751
 
  data->cache = cache;
752
 
  data->thumbnail = TRUE;
753
 
  data->recent_info = gtk_recent_info_ref (info);
754
 
  data->width = size;
755
 
  data->height = size;
756
 
  data->user_data = user_data;
757
 
 
758
 
  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_recent_thumbnail_async);
759
 
 
760
 
  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
761
 
  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
762
 
 
763
 
  g_object_unref (result);
764
 
}
765
 
 
766
582
static GdkPixbuf *
767
583
load_pixbuf_async_finish (StTextureCache *cache, GAsyncResult *result, GError **error)
768
584
{
772
588
  return g_simple_async_result_get_op_res_gpointer (simple);
773
589
}
774
590
 
775
 
typedef struct {
776
 
  StTextureCachePolicy policy;
777
 
  char *key;
778
 
  char *uri;
779
 
  gboolean thumbnail;
780
 
  gboolean enforced_square;
781
 
  char *mimetype;
782
 
  GtkRecentInfo *recent_info;
783
 
  char *checksum;
784
 
  GIcon *icon;
785
 
  GtkIconInfo *icon_info;
786
 
  guint width;
787
 
  guint height;
788
 
  GSList *textures;
789
 
} AsyncTextureLoadData;
790
 
 
791
591
static CoglHandle
792
592
pixbuf_to_cogl_handle (GdkPixbuf *pixbuf,
793
593
                       gboolean   add_padding)
853
653
  return surface;
854
654
}
855
655
 
856
 
static GdkPixbuf *
857
 
load_pixbuf_fallback(AsyncTextureLoadData *data)
858
 
{
859
 
  GdkPixbuf *pixbuf = NULL;
860
 
 
861
 
  if (data->thumbnail)
862
 
    {
863
 
 
864
 
      GtkIconTheme *theme = gtk_icon_theme_get_default ();
865
 
 
866
 
      if (data->recent_info)
867
 
          pixbuf = gtk_recent_info_get_icon (data->recent_info, data->width);
868
 
      else
869
 
        {
870
 
          GIcon *icon = icon_for_mimetype (data->mimetype);
871
 
          if (icon != NULL)
872
 
            {
873
 
              GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon (theme,
874
 
                                                                       icon,
875
 
                                                                       data->width,
876
 
                                                                       GTK_ICON_LOOKUP_USE_BUILTIN);
877
 
              g_object_unref (icon);
878
 
              if (icon_info != NULL)
879
 
                pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
880
 
            }
881
 
        }
882
 
 
883
 
      if (pixbuf == NULL)
884
 
        pixbuf = gtk_icon_theme_load_icon (theme,
885
 
                                           "gtk-file",
886
 
                                           data->width,
887
 
                                           GTK_ICON_LOOKUP_USE_BUILTIN,
888
 
                                           NULL);
889
 
    }
890
 
  /* Maybe we could need a fallback for outher image types? */
891
 
 
892
 
  return pixbuf;
893
 
}
894
 
 
895
656
static void
896
657
on_pixbuf_loaded (GObject      *source,
897
658
                  GAsyncResult *result,
911
672
 
912
673
  pixbuf = load_pixbuf_async_finish (cache, result, &error);
913
674
  if (pixbuf == NULL)
914
 
    pixbuf = load_pixbuf_fallback (data);
915
 
  if (pixbuf == NULL)
916
675
    goto out;
917
676
 
918
677
  texdata = pixbuf_to_cogl_handle (pixbuf, data->enforced_square);
941
700
out:
942
701
  if (texdata)
943
702
    cogl_handle_unref (texdata);
944
 
  g_free (data->key);
945
 
 
946
 
  if (data->icon)
947
 
    {
948
 
      gtk_icon_info_free (data->icon_info);
949
 
      g_object_unref (data->icon);
950
 
    }
951
 
  else if (data->uri)
952
 
    g_free (data->uri);
953
 
 
954
 
  if (data->recent_info)
955
 
    gtk_recent_info_unref (data->recent_info);
956
 
  if (data->mimetype)
957
 
    g_free (data->mimetype);
958
 
 
959
 
  /* Alternatively we could weakref and just do nothing if the texture
960
 
     is destroyed */
961
 
  for (iter = data->textures; iter; iter = iter->next)
962
 
    {
963
 
      ClutterTexture *texture = iter->data;
964
 
      g_object_unref (texture);
965
 
    }
 
703
 
 
704
  texture_load_data_destroy (data);
 
705
  g_free (data);
966
706
 
967
707
  g_clear_error (&error);
968
 
  g_free (data);
 
708
}
 
709
 
 
710
static void
 
711
load_texture_async (StTextureCache       *cache,
 
712
                    AsyncTextureLoadData *data)
 
713
{
 
714
  GSimpleAsyncResult *result;
 
715
  result = g_simple_async_result_new (G_OBJECT (cache), on_pixbuf_loaded, data, load_texture_async);
 
716
  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, NULL);
 
717
  g_object_unref (result);
969
718
}
970
719
 
971
720
typedef struct {
1182
931
  GtkIconInfo *info;
1183
932
  StTextureCachePolicy policy;
1184
933
 
 
934
  /* Do theme lookups in the main thread to avoid thread-unsafety */
 
935
  theme = cache->priv->icon_theme;
 
936
 
 
937
  info = gtk_icon_theme_lookup_by_gicon (theme, icon, size, GTK_ICON_LOOKUP_USE_BUILTIN);
 
938
  if (info == NULL)
 
939
    {
 
940
      /* gah, the icon doesn't exist. Return a blank texture that will never load */
 
941
      texture = CLUTTER_ACTOR (create_default_texture (cache));
 
942
      clutter_actor_set_size (texture, size, size);
 
943
      return texture;
 
944
    }
 
945
 
1185
946
  gicon_string = g_icon_to_string (icon);
1186
947
  /* A return value of NULL indicates that the icon can not be serialized,
1187
948
   * so don't have a unique identifier for it as a cache key, and thus can't
1208
969
 
1209
970
  if (create_texture_and_ensure_request (cache, key, size, policy, &request, &texture))
1210
971
    {
 
972
      /* If there's an outstanding request, we've just added ourselves to it */
1211
973
      g_free (key);
1212
 
      return texture;
1213
974
    }
1214
 
 
1215
 
  /* Do theme lookups in the main thread to avoid thread-unsafety */
1216
 
  theme = cache->priv->icon_theme;
1217
 
 
1218
 
  info = gtk_icon_theme_lookup_by_gicon (theme, icon, size, GTK_ICON_LOOKUP_USE_BUILTIN);
1219
 
  if (info != NULL)
 
975
  else
1220
976
    {
 
977
      /* Else, make a new request */
 
978
 
 
979
      request->cache = cache;
1221
980
      /* Transfer ownership of key */
1222
981
      request->key = key;
1223
982
      request->policy = policy;
1224
983
      request->icon = g_object_ref (icon);
 
984
      request->colors = colors ? st_icon_colors_ref (colors) : NULL;
1225
985
      request->icon_info = info;
1226
986
      request->width = request->height = size;
1227
987
      request->enforced_square = TRUE;
1228
988
 
1229
 
      load_icon_pixbuf_async (cache, icon, info, size, colors, NULL, on_pixbuf_loaded, request);
1230
 
    }
1231
 
  else
1232
 
    {
1233
 
      /* Blah; we failed to find the icon, but we've added our texture to the outstanding
1234
 
       * requests.  In that case, just undo what create_texture_and_ensure_request() did.
1235
 
       */
1236
 
       g_slist_foreach (request->textures, (GFunc) g_object_unref, NULL);
1237
 
       g_slist_free (request->textures);
1238
 
       g_free (request);
1239
 
       g_hash_table_remove (cache->priv->outstanding_requests, key);
1240
 
       g_free (key);
1241
 
       g_object_unref (texture);
1242
 
       texture = NULL;
 
989
      load_texture_async (cache, request);
1243
990
    }
1244
991
 
1245
992
  return CLUTTER_ACTOR (texture);
1271
1018
  return load_gicon_with_colors (cache, icon, size, theme_node ? st_theme_node_get_icon_colors (theme_node) : NULL);
1272
1019
}
1273
1020
 
1274
 
typedef struct {
1275
 
  gchar *path;
1276
 
  gint   grid_width, grid_height;
1277
 
  ClutterGroup *group;
1278
 
} AsyncImageData;
1279
 
 
1280
1021
static ClutterActor *
1281
1022
load_from_pixbuf (GdkPixbuf *pixbuf)
1282
1023
{
1297
1038
  return CLUTTER_ACTOR (texture);
1298
1039
}
1299
1040
 
 
1041
typedef struct {
 
1042
  gchar *path;
 
1043
  gint   grid_width, grid_height;
 
1044
  ClutterActor *actor;
 
1045
} AsyncImageData;
 
1046
 
 
1047
static void
 
1048
on_data_destroy (gpointer data)
 
1049
{
 
1050
  AsyncImageData *d = (AsyncImageData *)data;
 
1051
  g_free (d->path);
 
1052
  g_object_unref (d->actor);
 
1053
  g_free (d);
 
1054
}
 
1055
 
1300
1056
static void
1301
1057
on_sliced_image_loaded (GObject *source_object,
1302
1058
                        GAsyncResult *res,
1313
1069
    {
1314
1070
      ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data));
1315
1071
      clutter_actor_hide (actor);
1316
 
      clutter_container_add_actor (CLUTTER_CONTAINER (data->group), actor);
 
1072
      clutter_actor_add_child (data->actor, actor);
1317
1073
    }
1318
1074
}
1319
1075
 
1320
1076
static void
1321
 
on_data_destroy (gpointer data)
1322
 
{
1323
 
  AsyncImageData *d = (AsyncImageData *)data;
1324
 
  g_free (d->path);
1325
 
  g_object_unref (d->group);
1326
 
  g_free (d);
1327
 
}
1328
 
 
1329
 
static void
1330
1077
free_glist_unref_gobjects (gpointer p)
1331
1078
{
1332
1079
  GList *list = p;
1357
1104
 
1358
1105
  width = gdk_pixbuf_get_width (pix);
1359
1106
  height = gdk_pixbuf_get_height (pix);
1360
 
  for (y = 0; y < height; y += data->grid_width)
 
1107
  for (y = 0; y < height; y += data->grid_height)
1361
1108
    {
1362
 
      for (x = 0; x < width; x += data->grid_height)
 
1109
      for (x = 0; x < width; x += data->grid_width)
1363
1110
        {
1364
1111
          GdkPixbuf *pixbuf = gdk_pixbuf_new_subpixbuf (pix, x, y, data->grid_width, data->grid_height);
1365
1112
          g_assert (pixbuf != NULL);
1384
1131
 * note that the dimensions of the image loaded from @path 
1385
1132
 * should be a multiple of the specified grid dimensions.
1386
1133
 *
1387
 
 * Returns: (transfer none): A new ClutterGroup
 
1134
 * Returns: (transfer none): A new #ClutterActor
1388
1135
 */
1389
 
ClutterGroup *
 
1136
ClutterActor *
1390
1137
st_texture_cache_load_sliced_image (StTextureCache    *cache,
1391
1138
                                    const gchar       *path,
1392
1139
                                    gint               grid_width,
1394
1141
{
1395
1142
  AsyncImageData *data;
1396
1143
  GSimpleAsyncResult *result;
1397
 
  ClutterGroup *group = CLUTTER_GROUP (clutter_group_new ());
 
1144
  ClutterActor *actor = clutter_actor_new ();
1398
1145
 
1399
1146
  data = g_new0 (AsyncImageData, 1);
1400
1147
  data->grid_width = grid_width;
1401
1148
  data->grid_height = grid_height;
1402
1149
  data->path = g_strdup (path);
1403
 
  data->group = group;
1404
 
  g_object_ref (G_OBJECT (group));
 
1150
  data->actor = actor;
 
1151
  g_object_ref (G_OBJECT (actor));
1405
1152
 
1406
1153
  result = g_simple_async_result_new (G_OBJECT (cache), on_sliced_image_loaded, data, st_texture_cache_load_sliced_image);
1407
1154
 
1410
1157
 
1411
1158
  g_object_unref (result);
1412
1159
 
1413
 
  return group;
 
1160
  return actor;
1414
1161
}
1415
1162
 
1416
1163
/**
1481
1228
 * @theme_node: (allow-none): a #StThemeNode
1482
1229
 * @name: Name of a themed icon
1483
1230
 * @icon_type: the type of icon to load
1484
 
 * @size: Size of themed
 
1231
 * @size: Size of themed icon
1485
1232
 *
1486
1233
 * Load a themed icon into a texture. See the #StIconType documentation
1487
1234
 * for an explanation of how @icon_type affects the returned icon. The
1583
1330
  texture = create_default_texture (cache);
1584
1331
 
1585
1332
  data = g_new0 (AsyncTextureLoadData, 1);
 
1333
  data->cache = cache;
1586
1334
  data->key = g_strconcat (CACHE_PREFIX_URI, uri, NULL);
1587
1335
  data->policy = ST_TEXTURE_CACHE_POLICY_NONE;
1588
1336
  data->uri = g_strdup (uri);
1589
1337
  data->width = available_width;
1590
1338
  data->height = available_height;
1591
1339
  data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
1592
 
  load_uri_pixbuf_async (cache, uri, available_width, available_height, NULL, on_pixbuf_loaded, data);
 
1340
  load_texture_async (cache, data);
1593
1341
 
1594
1342
  return CLUTTER_ACTOR (texture);
1595
1343
}
1759
1507
 * into a cairo surface.  On error, a warning is emitted
1760
1508
 * and %NULL is returned.
1761
1509
 *
1762
 
 * Returns: (transfer full): a new #cairo_surface_t *
 
1510
 * Returns: (transfer full): a new #cairo_surface_t
1763
1511
 */
1764
1512
cairo_surface_t *
1765
1513
st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache,
1941
1689
  return CLUTTER_ACTOR (texture);
1942
1690
}
1943
1691
 
1944
 
/**
1945
 
 * st_texture_cache_load_thumbnail:
1946
 
 * @cache:
1947
 
 * @size: Size in pixels to use for thumbnail
1948
 
 * @uri: Source URI
1949
 
 * @mimetype: Source mime type
1950
 
 *
1951
 
 * Asynchronously load a thumbnail image of a URI into a texture.  The
1952
 
 * returned texture object will be a new instance; however, its texture data
1953
 
 * may be shared with other objects.  This implies the texture data is cached.
1954
 
 *
1955
 
 * The current caching policy is permanent; to uncache, you must explicitly
1956
 
 * call st_texture_cache_unref_thumbnail().
1957
 
 *
1958
 
 * Returns: (transfer none): A new #ClutterActor
1959
 
 */
1960
 
ClutterActor *
1961
 
st_texture_cache_load_thumbnail (StTextureCache    *cache,
1962
 
                                 int                size,
1963
 
                                 const char        *uri,
1964
 
                                 const char        *mimetype)
1965
 
{
1966
 
  ClutterTexture *texture;
1967
 
  AsyncTextureLoadData *data;
1968
 
  char *key;
1969
 
  CoglHandle texdata;
1970
 
 
1971
 
  /* Don't attempt to load thumbnails for non-local URIs */
1972
 
  if (!g_str_has_prefix (uri, "file://"))
1973
 
    {
1974
 
      GIcon *icon = icon_for_mimetype (mimetype);
1975
 
      return st_texture_cache_load_gicon (cache, NULL, icon, size);
1976
 
    }
1977
 
 
1978
 
  texture = create_default_texture (cache);
1979
 
  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
1980
 
 
1981
 
  key = g_strdup_printf (CACHE_PREFIX_THUMBNAIL_URI "uri=%s,size=%d", uri, size);
1982
 
 
1983
 
  texdata = g_hash_table_lookup (cache->priv->keyed_cache, key);
1984
 
  if (!texdata)
1985
 
    {
1986
 
      data = g_new0 (AsyncTextureLoadData, 1);
1987
 
      data->key = g_strdup (key);
1988
 
      data->policy = ST_TEXTURE_CACHE_POLICY_FOREVER;
1989
 
      data->uri = g_strdup (uri);
1990
 
      data->mimetype = g_strdup (mimetype);
1991
 
      data->thumbnail = TRUE;
1992
 
      data->width = size;
1993
 
      data->height = size;
1994
 
      data->enforced_square = TRUE;
1995
 
      data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
1996
 
      load_thumbnail_async (cache, uri, mimetype, size, NULL, on_pixbuf_loaded, data);
1997
 
    }
1998
 
  else
1999
 
    {
2000
 
      set_texture_cogl_texture (texture, texdata);
2001
 
    }
2002
 
 
2003
 
  g_free (key);
2004
 
  return CLUTTER_ACTOR (texture);
2005
 
}
2006
 
 
2007
1692
static GIcon *
2008
1693
icon_for_recent (GtkRecentInfo *info)
2009
1694
{
2018
1703
  return icon_for_mimetype (mimetype);
2019
1704
}
2020
1705
 
2021
 
/**
2022
 
 * st_texture_cache_load_recent_thumbnail:
2023
 
 * @cache:
2024
 
 * @size: Size in pixels to use for thumbnail
2025
 
 * @info: Recent item info
2026
 
 *
2027
 
 * Asynchronously load a thumbnail image of a #GtkRecentInfo into a texture.  The
2028
 
 * returned texture object will be a new instance; however, its texture data
2029
 
 * may be shared with other objects.  This implies the texture data is cached.
2030
 
 *
2031
 
 * The current caching policy is permanent; to uncache, you must explicitly
2032
 
 * call st_texture_cache_unref_recent_thumbnail().
2033
 
 *
2034
 
 * Returns: (transfer none): A new #ClutterActor
2035
 
 */
2036
 
ClutterActor *
2037
 
st_texture_cache_load_recent_thumbnail (StTextureCache    *cache,
2038
 
                                        int                size,
2039
 
                                        GtkRecentInfo     *info)
2040
 
{
2041
 
  ClutterTexture *texture;
2042
 
  AsyncTextureLoadData *data;
2043
 
  char *key;
2044
 
  CoglHandle texdata;
2045
 
  const char *uri;
2046
 
 
2047
 
  uri = gtk_recent_info_get_uri (info);
2048
 
 
2049
 
  /* Don't attempt to load thumbnails for non-local URIs */
2050
 
  if (!g_str_has_prefix (uri, "file://"))
2051
 
    {
2052
 
      GIcon *icon = icon_for_recent (info);
2053
 
      return st_texture_cache_load_gicon (cache, NULL, icon, size);
2054
 
    }
2055
 
 
2056
 
  texture = CLUTTER_TEXTURE (clutter_texture_new ());
2057
 
  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
2058
 
 
2059
 
  key = g_strdup_printf (CACHE_PREFIX_THUMBNAIL_URI "uri=%s,size=%d", uri, size);
2060
 
 
2061
 
  texdata = g_hash_table_lookup (cache->priv->keyed_cache, key);
2062
 
  if (!texdata)
2063
 
    {
2064
 
      data = g_new0 (AsyncTextureLoadData, 1);
2065
 
      data->key = g_strdup (key);
2066
 
      data->policy = ST_TEXTURE_CACHE_POLICY_FOREVER;
2067
 
      data->thumbnail = TRUE;
2068
 
      data->recent_info = gtk_recent_info_ref (info);
2069
 
      data->width = size;
2070
 
      data->height = size;
2071
 
      data->enforced_square = TRUE;
2072
 
      data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
2073
 
      load_recent_thumbnail_async (cache, info, size, NULL, on_pixbuf_loaded, data);
2074
 
    }
2075
 
  else
2076
 
    {
2077
 
      set_texture_cogl_texture (texture, texdata);
2078
 
    }
2079
 
 
2080
 
  g_free (key);
2081
 
  return CLUTTER_ACTOR (texture);
2082
 
}
2083
 
 
2084
 
/**
2085
 
 * st_texture_cache_evict_thumbnail:
2086
 
 * @cache:
2087
 
 * @uri: Source URI
2088
 
 *
2089
 
 * Removes all references added by st_texture_cache_load_thumbnail() function
2090
 
 * created for the given URI.
2091
 
 */
2092
 
void
2093
 
st_texture_cache_evict_thumbnail (StTextureCache    *cache,
2094
 
                                  const char        *uri)
2095
 
{
2096
 
  char *target_key;
2097
 
 
2098
 
  target_key = g_strconcat (CACHE_PREFIX_THUMBNAIL_URI, uri, NULL);
2099
 
  g_hash_table_remove (cache->priv->keyed_cache, target_key);
2100
 
  g_free (target_key);
2101
 
}
2102
 
 
2103
 
/**
2104
 
 * st_texture_cache_evict_recent_thumbnail:
2105
 
 * @cache:
2106
 
 * @info: A recent info
2107
 
 *
2108
 
 * Removes all references added by st_texture_cache_load_recent_thumbnail() function
2109
 
 * for the URI associated with the given @info.
2110
 
 */
2111
 
void
2112
 
st_texture_cache_evict_recent_thumbnail (StTextureCache *cache,
2113
 
                                         GtkRecentInfo  *info)
2114
 
{
2115
 
  st_texture_cache_evict_thumbnail (cache, gtk_recent_info_get_uri (info));
2116
 
}
2117
 
 
2118
1706
static size_t
2119
1707
pixbuf_byte_size (GdkPixbuf *pixbuf)
2120
1708
{