1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpimagefile.c
5 *
6 * Copyright (C) 2001-2004 Sven Neumann <sven@gimp.org>
7 * Michael Natterer <mitch@gimp.org>
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <gegl.h>
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29
30 #include "libgimpbase/gimpbase.h"
31 #include "libgimpthumb/gimpthumb.h"
32
33 #include "core-types.h"
34
35 #include "config/gimpcoreconfig.h"
36
37 #include "gegl/gimp-babl.h"
38 #include "gegl/gimp-gegl-utils.h"
39
40 #include "gimp.h"
41 #include "gimpcontainer.h"
42 #include "gimpcontext.h"
43 #include "gimpimage.h"
44 #include "gimpimagefile.h"
45 #include "gimpmarshal.h"
46 #include "gimppickable.h"
47 #include "gimpprogress.h"
48
49 #include "file/file-open.h"
50
51 #include "gimp-intl.h"
52
53
54 enum
55 {
56 INFO_CHANGED,
57 LAST_SIGNAL
58 };
59
60
61 typedef struct _GimpImagefilePrivate GimpImagefilePrivate;
62
63 struct _GimpImagefilePrivate
64 {
65 Gimp *gimp;
66
67 GFile *file;
68 GimpThumbnail *thumbnail;
69 GIcon *icon;
70 GCancellable *icon_cancellable;
71
72 gchar *description;
73 gboolean static_desc;
74 };
75
76 #define GET_PRIVATE(imagefile) ((GimpImagefilePrivate *) gimp_imagefile_get_instance_private ((GimpImagefile *) (imagefile)))
77
78
79 static void gimp_imagefile_dispose (GObject *object);
80 static void gimp_imagefile_finalize (GObject *object);
81
82 static void gimp_imagefile_name_changed (GimpObject *object);
83
84 static GdkPixbuf * gimp_imagefile_get_new_pixbuf (GimpViewable *viewable,
85 GimpContext *context,
86 gint width,
87 gint height);
88 static gchar * gimp_imagefile_get_description (GimpViewable *viewable,
89 gchar **tooltip);
90
91 static void gimp_imagefile_info_changed (GimpImagefile *imagefile);
92 static void gimp_imagefile_notify_thumbnail (GimpImagefile *imagefile,
93 GParamSpec *pspec);
94
95 static void gimp_imagefile_icon_callback (GObject *source_object,
96 GAsyncResult *result,
97 gpointer data);
98
99 static GdkPixbuf * gimp_imagefile_load_thumb (GimpImagefile *imagefile,
100 gint width,
101 gint height);
102 static gboolean gimp_imagefile_save_thumb (GimpImagefile *imagefile,
103 GimpImage *image,
104 gint size,
105 gboolean replace,
106 GError **error);
107
108 static void gimp_thumbnail_set_info_from_image (GimpThumbnail *thumbnail,
109 const gchar *mime_type,
110 GimpImage *image);
111 static void gimp_thumbnail_set_info (GimpThumbnail *thumbnail,
112 const gchar *mime_type,
113 gint width,
114 gint height,
115 const Babl *format,
116 gint num_layers);
117
118
119 G_DEFINE_TYPE_WITH_PRIVATE (GimpImagefile, gimp_imagefile, GIMP_TYPE_VIEWABLE)
120
121 #define parent_class gimp_imagefile_parent_class
122
123 static guint gimp_imagefile_signals[LAST_SIGNAL] = { 0 };
124
125
126 static void
gimp_imagefile_class_init(GimpImagefileClass * klass)127 gimp_imagefile_class_init (GimpImagefileClass *klass)
128 {
129 GObjectClass *object_class = G_OBJECT_CLASS (klass);
130 GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
131 GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
132 gchar *creator;
133
134 gimp_imagefile_signals[INFO_CHANGED] =
135 g_signal_new ("info-changed",
136 G_TYPE_FROM_CLASS (klass),
137 G_SIGNAL_RUN_FIRST,
138 G_STRUCT_OFFSET (GimpImagefileClass, info_changed),
139 NULL, NULL,
140 gimp_marshal_VOID__VOID,
141 G_TYPE_NONE, 0);
142
143 object_class->dispose = gimp_imagefile_dispose;
144 object_class->finalize = gimp_imagefile_finalize;
145
146 gimp_object_class->name_changed = gimp_imagefile_name_changed;
147
148 viewable_class->name_changed_signal = "info-changed";
149 viewable_class->get_new_pixbuf = gimp_imagefile_get_new_pixbuf;
150 viewable_class->get_description = gimp_imagefile_get_description;
151
152 g_type_class_ref (GIMP_TYPE_IMAGE_TYPE);
153
154 creator = g_strdup_printf ("gimp-%d.%d",
155 GIMP_MAJOR_VERSION, GIMP_MINOR_VERSION);
156
157 gimp_thumb_init (creator, NULL);
158
159 g_free (creator);
160 }
161
162 static void
gimp_imagefile_init(GimpImagefile * imagefile)163 gimp_imagefile_init (GimpImagefile *imagefile)
164 {
165 GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
166
167 private->thumbnail = gimp_thumbnail_new ();
168
169 g_signal_connect_object (private->thumbnail, "notify",
170 G_CALLBACK (gimp_imagefile_notify_thumbnail),
171 imagefile, G_CONNECT_SWAPPED);
172 }
173
174 static void
gimp_imagefile_dispose(GObject * object)175 gimp_imagefile_dispose (GObject *object)
176 {
177 GimpImagefilePrivate *private = GET_PRIVATE (object);
178
179 if (private->icon_cancellable)
180 {
181 g_cancellable_cancel (private->icon_cancellable);
182 g_clear_object (&private->icon_cancellable);
183 }
184
185 G_OBJECT_CLASS (parent_class)->dispose (object);
186 }
187
188 static void
gimp_imagefile_finalize(GObject * object)189 gimp_imagefile_finalize (GObject *object)
190 {
191 GimpImagefilePrivate *private = GET_PRIVATE (object);
192
193 if (private->description)
194 {
195 if (! private->static_desc)
196 g_free (private->description);
197
198 private->description = NULL;
199 }
200
201 g_clear_object (&private->thumbnail);
202 g_clear_object (&private->icon);
203 g_clear_object (&private->file);
204
205 G_OBJECT_CLASS (parent_class)->finalize (object);
206 }
207
208 static void
gimp_imagefile_name_changed(GimpObject * object)209 gimp_imagefile_name_changed (GimpObject *object)
210 {
211 GimpImagefilePrivate *private = GET_PRIVATE (object);
212
213 if (GIMP_OBJECT_CLASS (parent_class)->name_changed)
214 GIMP_OBJECT_CLASS (parent_class)->name_changed (object);
215
216 gimp_thumbnail_set_uri (private->thumbnail, gimp_object_get_name (object));
217
218 g_clear_object (&private->file);
219
220 if (gimp_object_get_name (object))
221 private->file = g_file_new_for_uri (gimp_object_get_name (object));
222 }
223
224 static GdkPixbuf *
gimp_imagefile_get_new_pixbuf(GimpViewable * viewable,GimpContext * context,gint width,gint height)225 gimp_imagefile_get_new_pixbuf (GimpViewable *viewable,
226 GimpContext *context,
227 gint width,
228 gint height)
229 {
230 GimpImagefile *imagefile = GIMP_IMAGEFILE (viewable);
231
232 if (! gimp_object_get_name (imagefile))
233 return NULL;
234
235 return gimp_imagefile_load_thumb (imagefile, width, height);
236 }
237
238 static gchar *
gimp_imagefile_get_description(GimpViewable * viewable,gchar ** tooltip)239 gimp_imagefile_get_description (GimpViewable *viewable,
240 gchar **tooltip)
241 {
242 GimpImagefile *imagefile = GIMP_IMAGEFILE (viewable);
243 GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
244 GimpThumbnail *thumbnail = private->thumbnail;
245 gchar *basename;
246
247 if (! private->file)
248 return NULL;
249
250 if (tooltip)
251 {
252 const gchar *name;
253 const gchar *desc;
254
255 name = gimp_file_get_utf8_name (private->file);
256 desc = gimp_imagefile_get_desc_string (imagefile);
257
258 if (desc)
259 *tooltip = g_strdup_printf ("%s\n%s", name, desc);
260 else
261 *tooltip = g_strdup (name);
262 }
263
264 basename = g_path_get_basename (gimp_file_get_utf8_name (private->file));
265
266 if (thumbnail->image_width > 0 && thumbnail->image_height > 0)
267 {
268 gchar *tmp = basename;
269
270 basename = g_strdup_printf ("%s (%d × %d)",
271 tmp,
272 thumbnail->image_width,
273 thumbnail->image_height);
274 g_free (tmp);
275 }
276
277 return basename;
278 }
279
280
281 /* public functions */
282
283 GimpImagefile *
gimp_imagefile_new(Gimp * gimp,GFile * file)284 gimp_imagefile_new (Gimp *gimp,
285 GFile *file)
286 {
287 GimpImagefile *imagefile;
288
289 g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
290 g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
291
292 imagefile = g_object_new (GIMP_TYPE_IMAGEFILE, NULL);
293
294 GET_PRIVATE (imagefile)->gimp = gimp;
295
296 if (file)
297 {
298 gimp_object_take_name (GIMP_OBJECT (imagefile), g_file_get_uri (file));
299
300 /* file member gets created by gimp_imagefile_name_changed() */
301 }
302
303 return imagefile;
304 }
305
306 GFile *
gimp_imagefile_get_file(GimpImagefile * imagefile)307 gimp_imagefile_get_file (GimpImagefile *imagefile)
308 {
309 g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
310
311 return GET_PRIVATE (imagefile)->file;
312 }
313
314 void
gimp_imagefile_set_file(GimpImagefile * imagefile,GFile * file)315 gimp_imagefile_set_file (GimpImagefile *imagefile,
316 GFile *file)
317 {
318 g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
319 g_return_if_fail (file == NULL || G_IS_FILE (file));
320
321 if (GET_PRIVATE (imagefile)->file != file)
322 {
323 gimp_object_take_name (GIMP_OBJECT (imagefile),
324 file ? g_file_get_uri (file) : NULL);
325 }
326 }
327
328 GimpThumbnail *
gimp_imagefile_get_thumbnail(GimpImagefile * imagefile)329 gimp_imagefile_get_thumbnail (GimpImagefile *imagefile)
330 {
331 g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
332
333 return GET_PRIVATE (imagefile)->thumbnail;
334 }
335
336 GIcon *
gimp_imagefile_get_gicon(GimpImagefile * imagefile)337 gimp_imagefile_get_gicon (GimpImagefile *imagefile)
338 {
339 GimpImagefilePrivate *private;
340
341 g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
342
343 private = GET_PRIVATE (imagefile);
344
345 if (private->icon)
346 return private->icon;
347
348 if (private->file && ! private->icon_cancellable)
349 {
350 private->icon_cancellable = g_cancellable_new ();
351
352 g_file_query_info_async (private->file, "standard::icon",
353 G_FILE_QUERY_INFO_NONE,
354 G_PRIORITY_DEFAULT,
355 private->icon_cancellable,
356 gimp_imagefile_icon_callback,
357 imagefile);
358 }
359
360 return NULL;
361 }
362
363 void
gimp_imagefile_set_mime_type(GimpImagefile * imagefile,const gchar * mime_type)364 gimp_imagefile_set_mime_type (GimpImagefile *imagefile,
365 const gchar *mime_type)
366 {
367 g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
368
369 g_object_set (GET_PRIVATE (imagefile)->thumbnail,
370 "image-mimetype", mime_type,
371 NULL);
372 }
373
374 void
gimp_imagefile_update(GimpImagefile * imagefile)375 gimp_imagefile_update (GimpImagefile *imagefile)
376 {
377 GimpImagefilePrivate *private;
378 gchar *uri;
379
380 g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
381
382 private = GET_PRIVATE (imagefile);
383
384 gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile));
385
386 g_object_get (private->thumbnail,
387 "image-uri", &uri,
388 NULL);
389
390 if (uri)
391 {
392 GimpImagefile *documents_imagefile = (GimpImagefile *)
393 gimp_container_get_child_by_name (private->gimp->documents, uri);
394
395 if (documents_imagefile != imagefile &&
396 GIMP_IS_IMAGEFILE (documents_imagefile))
397 gimp_viewable_invalidate_preview (GIMP_VIEWABLE (documents_imagefile));
398
399 g_free (uri);
400 }
401 }
402
403 gboolean
gimp_imagefile_create_thumbnail(GimpImagefile * imagefile,GimpContext * context,GimpProgress * progress,gint size,gboolean replace,GError ** error)404 gimp_imagefile_create_thumbnail (GimpImagefile *imagefile,
405 GimpContext *context,
406 GimpProgress *progress,
407 gint size,
408 gboolean replace,
409 GError **error)
410 {
411 GimpImagefilePrivate *private;
412 GimpThumbnail *thumbnail;
413 GimpThumbState image_state;
414
415 g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
416 g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE);
417 g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
418 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
419
420 /* thumbnailing is disabled, we successfully did nothing */
421 if (size < 1)
422 return TRUE;
423
424 private = GET_PRIVATE (imagefile);
425
426 thumbnail = private->thumbnail;
427
428 gimp_thumbnail_set_uri (thumbnail,
429 gimp_object_get_name (imagefile));
430
431 image_state = gimp_thumbnail_peek_image (thumbnail);
432
433 if (image_state == GIMP_THUMB_STATE_REMOTE ||
434 image_state >= GIMP_THUMB_STATE_EXISTS)
435 {
436 GimpImage *image;
437 gboolean success;
438 gint width = 0;
439 gint height = 0;
440 const gchar *mime_type = NULL;
441 const Babl *format = NULL;
442 gint num_layers = -1;
443
444 /* we only want to attempt thumbnailing on readable, regular files */
445 if (g_file_is_native (private->file))
446 {
447 GFileInfo *file_info;
448 gboolean regular;
449 gboolean readable;
450
451 file_info = g_file_query_info (private->file,
452 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
453 G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
454 G_FILE_QUERY_INFO_NONE,
455 NULL, NULL);
456
457 regular = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR);
458 readable = g_file_info_get_attribute_boolean (file_info,
459 G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
460
461 g_object_unref (file_info);
462
463 if (! (regular && readable))
464 return TRUE;
465 }
466
467 g_object_ref (imagefile);
468
469 /* don't pass the error, we're only interested in errors from
470 * actual thumbnail saving
471 */
472 image = file_open_thumbnail (private->gimp, context, progress,
473 private->file, size,
474 &mime_type, &width, &height,
475 &format, &num_layers, NULL);
476
477 if (image)
478 {
479 gimp_thumbnail_set_info (private->thumbnail,
480 mime_type, width, height,
481 format, num_layers);
482 }
483 else
484 {
485 GimpPDBStatusType status;
486
487 /* don't pass the error, we're only interested in errors
488 * from actual thumbnail saving
489 */
490 image = file_open_image (private->gimp, context, progress,
491 private->file,
492 private->file,
493 FALSE, NULL, GIMP_RUN_NONINTERACTIVE,
494 &status, &mime_type, NULL);
495
496 if (image)
497 gimp_thumbnail_set_info_from_image (private->thumbnail,
498 mime_type, image);
499 }
500
501 if (image)
502 {
503 success = gimp_imagefile_save_thumb (imagefile,
504 image, size, replace,
505 error);
506
507 g_object_unref (image);
508 }
509 else
510 {
511 success = gimp_thumbnail_save_failure (thumbnail,
512 "GIMP " GIMP_VERSION,
513 error);
514 gimp_imagefile_update (imagefile);
515 }
516
517 g_object_unref (imagefile);
518
519 if (! success)
520 {
521 g_object_set (thumbnail,
522 "thumb-state", GIMP_THUMB_STATE_FAILED,
523 NULL);
524 }
525
526 return success;
527 }
528
529 return TRUE;
530 }
531
532 /* The weak version doesn't ref the imagefile but deals gracefully
533 * with an imagefile that is destroyed while the thumbnail is
534 * created. This allows one to use this function w/o the need to
535 * block the user interface.
536 */
537 void
gimp_imagefile_create_thumbnail_weak(GimpImagefile * imagefile,GimpContext * context,GimpProgress * progress,gint size,gboolean replace)538 gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile,
539 GimpContext *context,
540 GimpProgress *progress,
541 gint size,
542 gboolean replace)
543 {
544 GimpImagefilePrivate *private;
545 GimpImagefile *local;
546
547 g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
548
549 if (size < 1)
550 return;
551
552 private = GET_PRIVATE (imagefile);
553
554 if (! private->file)
555 return;
556
557 local = gimp_imagefile_new (private->gimp, private->file);
558
559 g_object_add_weak_pointer (G_OBJECT (imagefile), (gpointer) &imagefile);
560
561 if (! gimp_imagefile_create_thumbnail (local, context, progress, size, replace,
562 NULL))
563 {
564 /* The weak version works on a local copy so the thumbnail
565 * status of the actual image is not properly updated in case of
566 * creation failure, thus it would end up in a generic
567 * GIMP_THUMB_STATE_NOT_FOUND, which is less informative.
568 */
569 g_object_set (private->thumbnail,
570 "thumb-state", GIMP_THUMB_STATE_FAILED,
571 NULL);
572 }
573
574 if (imagefile)
575 {
576 GFile *file = gimp_imagefile_get_file (imagefile);
577
578 if (file && g_file_equal (file, gimp_imagefile_get_file (local)))
579 {
580 gimp_imagefile_update (imagefile);
581 }
582
583 g_object_remove_weak_pointer (G_OBJECT (imagefile),
584 (gpointer) &imagefile);
585 }
586
587 g_object_unref (local);
588 }
589
590 gboolean
gimp_imagefile_check_thumbnail(GimpImagefile * imagefile)591 gimp_imagefile_check_thumbnail (GimpImagefile *imagefile)
592 {
593 GimpImagefilePrivate *private;
594 gint size;
595
596 g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
597
598 private = GET_PRIVATE (imagefile);
599
600 size = private->gimp->config->thumbnail_size;
601
602 if (size > 0)
603 {
604 GimpThumbState state;
605
606 state = gimp_thumbnail_check_thumb (private->thumbnail, size);
607
608 return (state == GIMP_THUMB_STATE_OK);
609 }
610
611 return TRUE;
612 }
613
614 gboolean
gimp_imagefile_save_thumbnail(GimpImagefile * imagefile,const gchar * mime_type,GimpImage * image,GError ** error)615 gimp_imagefile_save_thumbnail (GimpImagefile *imagefile,
616 const gchar *mime_type,
617 GimpImage *image,
618 GError **error)
619 {
620 GimpImagefilePrivate *private;
621 gint size;
622 gboolean success = TRUE;
623
624 g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
625 g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
626 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
627
628 private = GET_PRIVATE (imagefile);
629
630 size = private->gimp->config->thumbnail_size;
631
632 if (size > 0)
633 {
634 gimp_thumbnail_set_info_from_image (private->thumbnail,
635 mime_type, image);
636
637 success = gimp_imagefile_save_thumb (imagefile,
638 image, size, FALSE,
639 error);
640 }
641
642 return success;
643 }
644
645
646 /* private functions */
647
648 static void
gimp_imagefile_info_changed(GimpImagefile * imagefile)649 gimp_imagefile_info_changed (GimpImagefile *imagefile)
650 {
651 GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
652
653 if (private->description)
654 {
655 if (! private->static_desc)
656 g_free (private->description);
657
658 private->description = NULL;
659 }
660
661 g_clear_object (&private->icon);
662
663 g_signal_emit (imagefile, gimp_imagefile_signals[INFO_CHANGED], 0);
664 }
665
666 static void
gimp_imagefile_notify_thumbnail(GimpImagefile * imagefile,GParamSpec * pspec)667 gimp_imagefile_notify_thumbnail (GimpImagefile *imagefile,
668 GParamSpec *pspec)
669 {
670 if (strcmp (pspec->name, "image-state") == 0 ||
671 strcmp (pspec->name, "thumb-state") == 0)
672 {
673 gimp_imagefile_info_changed (imagefile);
674 }
675 }
676
677 static void
gimp_imagefile_icon_callback(GObject * source_object,GAsyncResult * result,gpointer data)678 gimp_imagefile_icon_callback (GObject *source_object,
679 GAsyncResult *result,
680 gpointer data)
681 {
682 GimpImagefile *imagefile;
683 GimpImagefilePrivate *private;
684 GFile *file = G_FILE (source_object);
685 GError *error = NULL;
686 GFileInfo *file_info;
687
688 file_info = g_file_query_info_finish (file, result, &error);
689
690 if (error)
691 {
692 /* we were cancelled from dispose() and the imagefile is
693 * long gone, bail out
694 */
695 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
696 {
697 g_clear_error (&error);
698 return;
699 }
700
701 #ifdef GIMP_UNSTABLE
702 g_printerr ("%s: %s\n", G_STRFUNC, error->message);
703 #endif
704
705 g_clear_error (&error);
706 }
707
708 imagefile = GIMP_IMAGEFILE (data);
709 private = GET_PRIVATE (imagefile);
710
711 if (file_info)
712 {
713 private->icon = g_object_ref (g_file_info_get_icon (file_info));
714 g_object_unref (file_info);
715 }
716
717 g_clear_object (&private->icon_cancellable);
718
719 if (private->icon)
720 gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile));
721 }
722
723 const gchar *
gimp_imagefile_get_desc_string(GimpImagefile * imagefile)724 gimp_imagefile_get_desc_string (GimpImagefile *imagefile)
725 {
726 GimpImagefilePrivate *private;
727 GimpThumbnail *thumbnail;
728
729 g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
730
731 private = GET_PRIVATE (imagefile);
732
733 if (private->description)
734 return (const gchar *) private->description;
735
736 thumbnail = private->thumbnail;
737
738 switch (thumbnail->image_state)
739 {
740 case GIMP_THUMB_STATE_UNKNOWN:
741 private->description = NULL;
742 private->static_desc = TRUE;
743 break;
744
745 case GIMP_THUMB_STATE_FOLDER:
746 private->description = (gchar *) _("Folder");
747 private->static_desc = TRUE;
748 break;
749
750 case GIMP_THUMB_STATE_SPECIAL:
751 private->description = (gchar *) _("Special File");
752 private->static_desc = TRUE;
753 break;
754
755 case GIMP_THUMB_STATE_NOT_FOUND:
756 private->description =
757 (gchar *) g_strerror (thumbnail->image_not_found_errno);
758 private->static_desc = TRUE;
759 break;
760
761 default:
762 {
763 GString *str = g_string_new (NULL);
764
765 if (thumbnail->image_state == GIMP_THUMB_STATE_REMOTE)
766 {
767 g_string_append (str, _("Remote File"));
768 }
769
770 if (thumbnail->image_filesize > 0)
771 {
772 gchar *size = g_format_size (thumbnail->image_filesize);
773
774 if (str->len > 0)
775 g_string_append_c (str, '\n');
776
777 g_string_append (str, size);
778 g_free (size);
779 }
780
781 switch (thumbnail->thumb_state)
782 {
783 case GIMP_THUMB_STATE_NOT_FOUND:
784 if (str->len > 0)
785 g_string_append_c (str, '\n');
786 g_string_append (str, _("Click to create preview"));
787 break;
788
789 case GIMP_THUMB_STATE_EXISTS:
790 if (str->len > 0)
791 g_string_append_c (str, '\n');
792 g_string_append (str, _("Loading preview..."));
793 break;
794
795 case GIMP_THUMB_STATE_OLD:
796 if (str->len > 0)
797 g_string_append_c (str, '\n');
798 g_string_append (str, _("Preview is out of date"));
799 break;
800
801 case GIMP_THUMB_STATE_FAILED:
802 if (str->len > 0)
803 g_string_append_c (str, '\n');
804 g_string_append (str, _("Cannot create preview"));
805 break;
806
807 case GIMP_THUMB_STATE_OK:
808 {
809 if (thumbnail->image_state == GIMP_THUMB_STATE_REMOTE)
810 {
811 if (str->len > 0)
812 g_string_append_c (str, '\n');
813
814 g_string_append (str, _("(Preview may be out of date)"));
815 }
816
817 if (thumbnail->image_width > 0 && thumbnail->image_height > 0)
818 {
819 if (str->len > 0)
820 g_string_append_c (str, '\n');
821
822 g_string_append_printf (str,
823 ngettext ("%d × %d pixel",
824 "%d × %d pixels",
825 thumbnail->image_height),
826 thumbnail->image_width,
827 thumbnail->image_height);
828 }
829
830 if (thumbnail->image_type)
831 {
832 if (str->len > 0)
833 g_string_append_c (str, '\n');
834
835 g_string_append (str, gettext (thumbnail->image_type));
836 }
837
838 if (thumbnail->image_num_layers > 0)
839 {
840 if (thumbnail->image_type)
841 g_string_append_len (str, ", ", 2);
842 else if (str->len > 0)
843 g_string_append_c (str, '\n');
844
845 g_string_append_printf (str,
846 ngettext ("%d layer",
847 "%d layers",
848 thumbnail->image_num_layers),
849 thumbnail->image_num_layers);
850 }
851 }
852 break;
853
854 default:
855 break;
856 }
857
858 private->description = g_string_free (str, FALSE);
859 private->static_desc = FALSE;
860 }
861 }
862
863 return (const gchar *) private->description;
864 }
865
866 static GdkPixbuf *
gimp_imagefile_load_thumb(GimpImagefile * imagefile,gint width,gint height)867 gimp_imagefile_load_thumb (GimpImagefile *imagefile,
868 gint width,
869 gint height)
870 {
871 GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
872 GimpThumbnail *thumbnail = private->thumbnail;
873 GdkPixbuf *pixbuf = NULL;
874 GError *error = NULL;
875 gint size = MAX (width, height);
876 gint pixbuf_width;
877 gint pixbuf_height;
878 gint preview_width;
879 gint preview_height;
880
881 if (gimp_thumbnail_peek_thumb (thumbnail, size) < GIMP_THUMB_STATE_EXISTS)
882 return NULL;
883
884 if (thumbnail->image_state == GIMP_THUMB_STATE_NOT_FOUND)
885 return NULL;
886
887 pixbuf = gimp_thumbnail_load_thumb (thumbnail, size, &error);
888
889 if (! pixbuf)
890 {
891 if (error)
892 {
893 gimp_message (private->gimp, NULL, GIMP_MESSAGE_ERROR,
894 _("Could not open thumbnail '%s': %s"),
895 thumbnail->thumb_filename, error->message);
896 g_clear_error (&error);
897 }
898
899 return NULL;
900 }
901
902 pixbuf_width = gdk_pixbuf_get_width (pixbuf);
903 pixbuf_height = gdk_pixbuf_get_height (pixbuf);
904
905 gimp_viewable_calc_preview_size (pixbuf_width,
906 pixbuf_height,
907 width,
908 height,
909 TRUE, 1.0, 1.0,
910 &preview_width,
911 &preview_height,
912 NULL);
913
914 if (preview_width < pixbuf_width || preview_height < pixbuf_height)
915 {
916 GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf,
917 preview_width,
918 preview_height,
919 GDK_INTERP_BILINEAR);
920 g_object_unref (pixbuf);
921 pixbuf = scaled;
922
923 pixbuf_width = preview_width;
924 pixbuf_height = preview_height;
925 }
926
927 if (gdk_pixbuf_get_n_channels (pixbuf) != 3)
928 {
929 GdkPixbuf *tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
930 pixbuf_width, pixbuf_height);
931
932 gdk_pixbuf_composite_color (pixbuf, tmp,
933 0, 0, pixbuf_width, pixbuf_height,
934 0.0, 0.0, 1.0, 1.0,
935 GDK_INTERP_NEAREST, 255,
936 0, 0, GIMP_CHECK_SIZE_SM,
937 0x66666666, 0x99999999);
938
939 g_object_unref (pixbuf);
940 pixbuf = tmp;
941 }
942
943 return pixbuf;
944 }
945
946 static gboolean
gimp_imagefile_save_thumb(GimpImagefile * imagefile,GimpImage * image,gint size,gboolean replace,GError ** error)947 gimp_imagefile_save_thumb (GimpImagefile *imagefile,
948 GimpImage *image,
949 gint size,
950 gboolean replace,
951 GError **error)
952 {
953 GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
954 GimpThumbnail *thumbnail = private->thumbnail;
955 GdkPixbuf *pixbuf;
956 gint width, height;
957 gboolean success = FALSE;
958
959 if (size < 1)
960 return TRUE;
961
962 if (gimp_image_get_width (image) <= size &&
963 gimp_image_get_height (image) <= size)
964 {
965 width = gimp_image_get_width (image);
966 height = gimp_image_get_height (image);
967
968 size = MAX (width, height);
969 }
970 else
971 {
972 if (gimp_image_get_width (image) < gimp_image_get_height (image))
973 {
974 height = size;
975 width = MAX (1, (size * gimp_image_get_width (image) /
976 gimp_image_get_height (image)));
977 }
978 else
979 {
980 width = size;
981 height = MAX (1, (size * gimp_image_get_height (image) /
982 gimp_image_get_width (image)));
983 }
984 }
985
986 /* we need the projection constructed NOW, not some time later */
987 gimp_pickable_flush (GIMP_PICKABLE (image));
988
989 pixbuf = gimp_viewable_get_new_pixbuf (GIMP_VIEWABLE (image),
990 /* random context, unused */
991 gimp_get_user_context (image->gimp),
992 width, height);
993
994 /* when layer previews are disabled, we won't get a pixbuf */
995 if (! pixbuf)
996 return TRUE;
997
998 success = gimp_thumbnail_save_thumb (thumbnail,
999 pixbuf,
1000 "GIMP " GIMP_VERSION,
1001 error);
1002
1003 g_object_unref (pixbuf);
1004
1005 if (success)
1006 {
1007 if (replace)
1008 gimp_thumbnail_delete_others (thumbnail, size);
1009 else
1010 gimp_thumbnail_delete_failure (thumbnail);
1011
1012 gimp_imagefile_update (imagefile);
1013 }
1014
1015 return success;
1016 }
1017
1018 static void
gimp_thumbnail_set_info_from_image(GimpThumbnail * thumbnail,const gchar * mime_type,GimpImage * image)1019 gimp_thumbnail_set_info_from_image (GimpThumbnail *thumbnail,
1020 const gchar *mime_type,
1021 GimpImage *image)
1022 {
1023 const Babl *format;
1024
1025 /* peek the thumbnail to make sure that mtime and filesize are set */
1026 gimp_thumbnail_peek_image (thumbnail);
1027
1028 format = gimp_image_get_layer_format (image,
1029 gimp_image_has_alpha (image));
1030
1031 g_object_set (thumbnail,
1032 "image-mimetype", mime_type,
1033 "image-width", gimp_image_get_width (image),
1034 "image-height", gimp_image_get_height (image),
1035 "image-type", gimp_babl_format_get_description (format),
1036 "image-num-layers", gimp_image_get_n_layers (image),
1037 NULL);
1038 }
1039
1040 /**
1041 * gimp_thumbnail_set_info:
1042 * @thumbnail: #GimpThumbnail object
1043 * @mime_type: MIME type of the image associated with this thumbnail
1044 * @width: width of the image associated with this thumbnail
1045 * @height: height of the image associated with this thumbnail
1046 * @format: format of the image (or NULL if the type is not known)
1047 * @num_layers: number of layers in the image
1048 * (or -1 if the number of layers is not known)
1049 *
1050 * Set information about the image associated with the @thumbnail object.
1051 */
1052 static void
gimp_thumbnail_set_info(GimpThumbnail * thumbnail,const gchar * mime_type,gint width,gint height,const Babl * format,gint num_layers)1053 gimp_thumbnail_set_info (GimpThumbnail *thumbnail,
1054 const gchar *mime_type,
1055 gint width,
1056 gint height,
1057 const Babl *format,
1058 gint num_layers)
1059 {
1060 /* peek the thumbnail to make sure that mtime and filesize are set */
1061 gimp_thumbnail_peek_image (thumbnail);
1062
1063 g_object_set (thumbnail,
1064 "image-mimetype", mime_type,
1065 "image-width", width,
1066 "image-height", height,
1067 NULL);
1068
1069 if (format)
1070 g_object_set (thumbnail,
1071 "image-type", gimp_babl_format_get_description (format),
1072 NULL);
1073
1074 if (num_layers != -1)
1075 g_object_set (thumbnail,
1076 "image-num-layers", num_layers,
1077 NULL);
1078 }
1079