diff --git a/app/core/gimpimage-metadata.c b/app/core/gimpimage-metadata.c index ae0774f1bf..7adb9ccf18 100644 --- a/app/core/gimpimage-metadata.c +++ b/app/core/gimpimage-metadata.c @@ -20,17 +20,22 @@ #include #include #include +#include #include "libgimpbase/gimpbase.h" #include "libgimpcolor/gimpcolor.h" #include "core-types.h" +#include "gimp.h" #include "gimpimage.h" #include "gimpimage-color-profile.h" #include "gimpimage-metadata.h" #include "gimpimage-private.h" +#include "gimpimage-rotate.h" +#include "gimpimage-undo.h" #include "gimpimage-undo-push.h" +#include "gimplayer-new.h" /* public functions */ @@ -182,3 +187,110 @@ gimp_image_metadata_update_colorspace (GimpImage *image) gimp_metadata_set_colorspace (metadata, space); } } + +/** + * gimp_image_metadata_load_thumbnail: + * @gimp: The #Gimp object. + * @file: A #GFile image. + * @full_image_width: the width of the full image (not the thumbnail). + * @full_image_height: the height of the full image (not the thumbnail). + * @error: Return location for error message + * + * Retrieves a thumbnail from metadata in @file if present. + * It does not need to actually load the full image, only the metadata through + * GExiv2, which makes it fast. + * + * Returns: (transfer none) (nullable): a #GimpImage of the @file thumbnail. + */ +GimpImage * +gimp_image_metadata_load_thumbnail (Gimp *gimp, + GFile *file, + gint *full_image_width, + gint *full_image_height, + const Babl **format, + GError **error) +{ + GimpMetadata *metadata; + GInputStream *input_stream; + GdkPixbuf *pixbuf; + guint8 *thumbnail_buffer; + gint thumbnail_size; + GimpImage *image = NULL; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + metadata = gimp_metadata_load_from_file (file, error); + if (! metadata) + return NULL; + + if (! gexiv2_metadata_get_exif_thumbnail (GEXIV2_METADATA (metadata), + &thumbnail_buffer, + &thumbnail_size)) + { + g_object_unref (metadata); + return NULL; + } + + input_stream = g_memory_input_stream_new_from_data (thumbnail_buffer, + thumbnail_size, + (GDestroyNotify) g_free); + pixbuf = gdk_pixbuf_new_from_stream (input_stream, NULL, error); + g_object_unref (input_stream); + + if (pixbuf) + { + GimpLayer *layer; + + image = gimp_image_new (gimp, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + GIMP_RGB, GIMP_PRECISION_U8_NON_LINEAR); + gimp_image_undo_disable (image); + + /* XXX This is possibly wrong, because an image of a given color space may + * have a thumbnail stored in a different colorspace. This is even more + * true with GIMP which always exports RGB thumbnails (see code in + * gimp_image_metadata_save_filter()), even for say grayscale images. + * Nevertheless other software may store thumbnail using the same + * colorspace. + */ + *format = gimp_pixbuf_get_format (pixbuf); + layer = gimp_layer_new_from_pixbuf (pixbuf, image, + gimp_image_get_layer_format (image, FALSE), + /* No need to localize; this image is short-lived. */ + "Background", + GIMP_OPACITY_OPAQUE, + gimp_image_get_default_new_layer_mode (image)); + g_object_unref (pixbuf); + + gimp_image_add_layer (image, layer, NULL, 0, FALSE); + + gimp_image_apply_metadata_orientation (image, gimp_get_user_context (gimp), metadata, NULL); + } + + /* This is the unoriented dimensions. Should we switch when there is a 90 or + * 270 degree rotation? We don't actually know if the metadata orientation is + * correct. + */ + *full_image_width = gexiv2_metadata_get_pixel_width (GEXIV2_METADATA (metadata)); + *full_image_height = gexiv2_metadata_get_pixel_height (GEXIV2_METADATA (metadata)); + + if (*full_image_width < 1 || *full_image_height < 1) + { + /* Dimensions stored in metadata might be less accurate, yet it's still + * informational. + */ + *full_image_width = gexiv2_metadata_try_get_metadata_pixel_width (GEXIV2_METADATA (metadata), NULL); + *full_image_height = gexiv2_metadata_try_get_metadata_pixel_width (GEXIV2_METADATA (metadata), NULL); + } + if (*full_image_width < 1 || *full_image_height < 1) + { + *full_image_width = 0; + *full_image_height = 0; + } + + g_object_unref (metadata); + + return image; +} diff --git a/app/core/gimpimage-metadata.h b/app/core/gimpimage-metadata.h index 2a74be8c88..347a30022a 100644 --- a/app/core/gimpimage-metadata.h +++ b/app/core/gimpimage-metadata.h @@ -29,5 +29,12 @@ void gimp_image_metadata_update_bits_per_sample (GimpImage *image); void gimp_image_metadata_update_resolution (GimpImage *image); void gimp_image_metadata_update_colorspace (GimpImage *image); +GimpImage * gimp_image_metadata_load_thumbnail (Gimp *gimp, + GFile *file, + gint *full_image_width, + gint *full_image_height, + const Babl **format, + GError **error); + #endif /* __GIMP_IMAGE_METADATA_H__ */ diff --git a/app/core/gimpimage-rotate.c b/app/core/gimpimage-rotate.c index 535225c569..36610e9e94 100644 --- a/app/core/gimpimage-rotate.c +++ b/app/core/gimpimage-rotate.c @@ -278,6 +278,17 @@ gimp_image_import_rotation_metadata (GimpImage *image, } } +void +gimp_image_apply_metadata_orientation (GimpImage *image, + GimpContext *context, + GimpMetadata *metadata, + GimpProgress *progress) +{ + gimp_image_metadata_rotate (image, context, + gexiv2_metadata_try_get_orientation (GEXIV2_METADATA (metadata), NULL), + progress); +} + /* Private Functions */ diff --git a/app/core/gimpimage-rotate.h b/app/core/gimpimage-rotate.h index 7a8f812ac6..63f493dbfe 100644 --- a/app/core/gimpimage-rotate.h +++ b/app/core/gimpimage-rotate.h @@ -19,14 +19,20 @@ #define __GIMP_IMAGE_ROTATE_H__ -void gimp_image_rotate (GimpImage *image, - GimpContext *context, - GimpRotationType rotate_type, - GimpProgress *progress); +void gimp_image_rotate (GimpImage *image, + GimpContext *context, + GimpRotationType rotate_type, + GimpProgress *progress); + +void gimp_image_import_rotation_metadata (GimpImage *image, + GimpContext *context, + GimpProgress *progress, + gboolean interactive); + +void gimp_image_apply_metadata_orientation (GimpImage *image, + GimpContext *context, + GimpMetadata *metadata, + GimpProgress *progress); -void gimp_image_import_rotation_metadata (GimpImage *image, - GimpContext *context, - GimpProgress *progress, - gboolean interactive); #endif /* __GIMP_IMAGE_ROTATE_H__ */ diff --git a/app/core/gimpimagefile.c b/app/core/gimpimagefile.c index 194e042b34..a859ba86c6 100644 --- a/app/core/gimpimagefile.c +++ b/app/core/gimpimagefile.c @@ -41,6 +41,7 @@ #include "gimpcontainer.h" #include "gimpcontext.h" #include "gimpimage.h" +#include "gimpimage-metadata.h" #include "gimpimagefile.h" #include "gimppickable.h" #include "gimpprogress.h" @@ -482,19 +483,37 @@ gimp_imagefile_create_thumbnail (GimpImagefile *imagefile, if (error && *error) { g_printerr ("Info: Thumbnail load procedure failed: %s\n" - " Falling back to file load procedure.\n", + " Falling back to metadata or file load.\n", (*error)->message); g_clear_error (error); } - image = file_open_image (private->gimp, context, progress, - private->file, - FALSE, NULL, GIMP_RUN_NONINTERACTIVE, - &status, &mime_type, error); - + image = gimp_image_metadata_load_thumbnail (private->gimp, private->file, &width, &height, &format, error); if (image) - gimp_thumbnail_set_info_from_image (private->thumbnail, - mime_type, image); + { + gimp_thumbnail_set_info (private->thumbnail, + mime_type, width, height, + format, 0); + } + else + { + if (error && *error) + { + g_printerr ("Info: metadata load failed: %s\n" + " Falling back to file load procedure.\n", + (*error)->message); + g_clear_error (error); + } + + image = file_open_image (private->gimp, context, progress, + private->file, + FALSE, NULL, GIMP_RUN_NONINTERACTIVE, + &status, &mime_type, error); + + if (image) + gimp_thumbnail_set_info_from_image (private->thumbnail, + mime_type, image); + } } if (image) diff --git a/libgimp/gimpimagemetadata-save.c b/libgimp/gimpimagemetadata-save.c index 0bb3cb7fa5..f9ff59b7a9 100644 --- a/libgimp/gimpimagemetadata-save.c +++ b/libgimp/gimpimagemetadata-save.c @@ -887,6 +887,11 @@ gimp_image_metadata_save_filter (GimpImage *image, thumbw = EXIF_THUMBNAIL_SIZE * image_width / image_height; } + /* TODO: currently our exported thumbnails are always RGB because + * GdkPixbuf only supports RGB so far. While RGB thumbnail may make sense, + * even for CMYK images or other color spaces, grayscale images would be + * better served with a grayscale thumbnail too. + */ thumb_pixbuf = gimp_image_get_thumbnail (image, thumbw, thumbh, GIMP_PIXBUF_KEEP_ALPHA);