diff options
-rw-r--r-- | fiv-thumbnail.c | 184 |
1 files changed, 108 insertions, 76 deletions
diff --git a/fiv-thumbnail.c b/fiv-thumbnail.c index 0f6d264..51e53b2 100644 --- a/fiv-thumbnail.c +++ b/fiv-thumbnail.c @@ -240,71 +240,68 @@ orient_thumbnail(cairo_surface_t *surface, FivIoOrientation orientation) return oriented; } -cairo_surface_t * -fiv_thumbnail_extract(GFile *target, FivThumbnailSize max_size, GError **error) +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#ifdef HAVE_LIBRAW + +static cairo_surface_t * +extract_libraw_bitmap( + libraw_data_t *iprc, libraw_processed_image_t *image, GError **error) { - const char *path = g_file_peek_path(target); - if (!path) { - set_error(error, "thumbnails will only be extracted from local files"); + // Anything else is extremely rare. + if (image->colors != 3 || image->bits != 8) { + set_error(error, "unsupported bitmap thumbnail"); return NULL; } - GMappedFile *mf = g_mapped_file_new(path, FALSE, error); - if (!mf) - return NULL; - - // Bitmap thumbnails generally need rotating, e.g.: - // - Hasselblad/H4D-50/2-9-2017_street_0012.fff - // - OnePlus/One/IMG_20150729_201116.dng (and more DNGs in general) - // Though it's apparent LibRaw doesn't adjust the thumbnails to match - // the main image's "flip" field (it just happens to match up often), e.g.: - // - Phase One/H 25/H25_Outdoor_.IIQ (correct Orientation in IFD0) - // - Phase One/H 25/H25_IT8.7-2_Card.TIF (correctly missing in IFD0) - // - // JPEG thumbnails generally have the right rotation in their Exif, e.g.: - // - Canon/EOS-1Ds Mark II/RAW_CANON_1DSM2.CR2 - // - Leica/C (Typ 112)/Leica_-_C_(Typ_112)-_3:2.RWL - // - Nikon/1 S2/RAW_NIKON_1S2.NEF - // - Panasonic/DMC-FZ18/RAW_PANASONIC_LUMIX_FZ18.RAW - // - Panasonic/DMC-FZ70/P1000836.RW2 - // - Samsung/NX200/2013-05-08-194524__sam6589.srw - // - Sony/DSC-HX95/DSC00018.ARW - // - // Some files are problematic and we won't bother with special-casing: - // - Leaf/Aptus 22/L_003172.mos (JPEG)'s thumbnail wrongly contains - // Exif Orientation 6, and sizes.flip also contains 6. - // - Nokia/Lumia 1020/RAW_NOKIA_LUMIA_1020.DNG (bitmap) has wrong color. - // - Ricoh/GXR/R0017428.DNG (JPEG) seems to be plainly invalid. - FivIoOrientation orientation = FivIoOrientationUnknown; - cairo_surface_t *surface = NULL; -#ifndef HAVE_LIBRAW - // TODO(p): Implement our own thumbnail extractors. - set_error(error, "unsupported file"); -#else // HAVE_LIBRAW - // In this case, g_mapped_file_get_contents() returns NULL, causing issues. - if (!g_mapped_file_get_length(mf)) { - set_error(error, "empty file"); - goto fail; + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_RGB24, image->width, image->height); + guint32 *out = (guint32 *) cairo_image_surface_get_data(surface); + const unsigned char *in = image->data; + for (guint64 i = 0; i < image->width * image->height; in += 3) + out[i++] = in[0] << 16 | in[1] << 8 | in[2]; + cairo_surface_mark_dirty(surface); + + // LibRaw actually turns an 8 to 5, so follow the documentation. + FivIoOrientation orient = FivIoOrientationUnknown; + switch (iprc->sizes.flip) { + break; case 3: + orient = FivIoOrientation180; + break; case 5: + orient = FivIoOrientation270; + break; case 6: + orient = FivIoOrientation90; + break; default: + return surface; } + cairo_surface_set_user_data( + surface, &fiv_io_key_orientation, (void *) (intptr_t) orient, NULL); + return surface; +} + +static cairo_surface_t * +extract_libraw(GFile *target, GMappedFile *mf, GError **error) +{ + cairo_surface_t *surface = NULL; libraw_data_t *iprc = libraw_init( LIBRAW_OPIONS_NO_MEMERR_CALLBACK | LIBRAW_OPIONS_NO_DATAERR_CALLBACK); if (!iprc) { set_error(error, "failed to obtain a LibRaw handle"); - goto fail; + return NULL; } int err = 0; if ((err = libraw_open_buffer(iprc, (void *) g_mapped_file_get_contents(mf), g_mapped_file_get_length(mf)))) { set_error(error, libraw_strerror(err)); - goto fail_libraw; + goto fail; } #if LIBRAW_VERSION >= LIBRAW_MAKE_VERSION(0, 21, 0) if (!iprc->thumbs_list.thumbcount) { set_error(error, "no thumbnails found"); - goto fail_libraw; + goto fail; } // The old libraw_unpack_thumb() goes for the largest thumbnail, @@ -323,66 +320,101 @@ fiv_thumbnail_extract(GFile *target, FivThumbnailSize max_size, GError **error) } if ((err = libraw_unpack_thumb_ex(iprc, best_index))) { set_error(error, libraw_strerror(err)); - goto fail_libraw; + goto fail; } -#else +#else // LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0) if ((err = libraw_unpack_thumb(iprc))) { set_error(error, libraw_strerror(err)); - goto fail_libraw; + goto fail; } -#endif +#endif // LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0) libraw_processed_image_t *image = libraw_dcraw_make_mem_thumb(iprc, &err); if (!image) { set_error(error, libraw_strerror(err)); - goto fail_libraw; + goto fail; } - gboolean dummy = FALSE; + // Bitmap thumbnails generally need rotating, e.g.: + // - Hasselblad/H4D-50/2-9-2017_street_0012.fff + // - OnePlus/One/IMG_20150729_201116.dng (and more DNGs in general) + // Though it's apparent LibRaw doesn't adjust the thumbnails to match + // the main image's "flip" field (it just happens to match up often), e.g.: + // - Phase One/H 25/H25_Outdoor_.IIQ (correct Orientation in IFD0) + // - Phase One/H 25/H25_IT8.7-2_Card.TIF (correctly missing in IFD0) + // + // JPEG thumbnails generally have the right rotation in their Exif, e.g.: + // - Canon/EOS-1Ds Mark II/RAW_CANON_1DSM2.CR2 + // - Leica/C (Typ 112)/Leica_-_C_(Typ_112)-_3:2.RWL + // - Nikon/1 S2/RAW_NIKON_1S2.NEF + // - Panasonic/DMC-FZ18/RAW_PANASONIC_LUMIX_FZ18.RAW + // - Panasonic/DMC-FZ70/P1000836.RW2 + // - Samsung/NX200/2013-05-08-194524__sam6589.srw + // - Sony/DSC-HX95/DSC00018.ARW + // + // Some files are problematic and we won't bother with special-casing: + // - Leaf/Aptus 22/L_003172.mos (JPEG)'s thumbnail wrongly contains + // Exif Orientation 6, and sizes.flip also contains 6. + // - Nokia/Lumia 1020/RAW_NOKIA_LUMIA_1020.DNG (bitmap) has wrong color. + // - Ricoh/GXR/R0017428.DNG (JPEG) seems to be plainly invalid. switch (image->type) { + gboolean dummy; case LIBRAW_IMAGE_JPEG: surface = render( target, g_bytes_new(image->data, image->data_size), &dummy, error); - orientation = (int) (intptr_t) cairo_surface_get_user_data( - surface, &fiv_io_key_orientation); break; case LIBRAW_IMAGE_BITMAP: - // Anything else is extremely rare. - if (image->colors != 3 || image->bits != 8) { - set_error(error, "unsupported bitmap thumbnail"); - break; - } - - surface = cairo_image_surface_create( - CAIRO_FORMAT_RGB24, image->width, image->height); - guint32 *out = (guint32 *) cairo_image_surface_get_data(surface); - const unsigned char *in = image->data; - for (guint64 i = 0; i < image->width * image->height; in += 3) - out[i++] = in[0] << 16 | in[1] << 8 | in[2]; - cairo_surface_mark_dirty(surface); - - // LibRaw actually turns an 8 to 5, so follow the documentation. - switch (iprc->sizes.flip) { - break; case 3: orientation = FivIoOrientation180; - break; case 5: orientation = FivIoOrientation270; - break; case 6: orientation = FivIoOrientation90; - } + surface = extract_libraw_bitmap(iprc, image, error); break; default: set_error(error, "unsupported embedded thumbnail"); } libraw_dcraw_clear_mem(image); -fail_libraw: +fail: libraw_close(iprc); + return surface; +} + #endif // HAVE_LIBRAW -fail: +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +cairo_surface_t * +fiv_thumbnail_extract(GFile *target, FivThumbnailSize max_size, GError **error) +{ + const char *path = g_file_peek_path(target); + if (!path) { + set_error(error, "thumbnails will only be extracted from local files"); + return NULL; + } + + GMappedFile *mf = g_mapped_file_new(path, FALSE, error); + if (!mf) + return NULL; + + // In this case, g_mapped_file_get_contents() returns NULL, causing issues. + if (!g_mapped_file_get_length(mf)) { + set_error(error, "empty file"); + return NULL; + } + + cairo_surface_t *surface = NULL; +#ifdef HAVE_LIBRAW + surface = extract_libraw(target, mf, error); +#else // ! HAVE_LIBRAW + // TODO(p): Implement our own thumbnail extractors. + set_error(error, "unsupported file"); +#endif // ! HAVE_LIBRAW g_mapped_file_unref(mf); - // This hardcodes Exif orientation before adjust_thumbnail() might do so, + // Hardcode Exif orientation before adjust_thumbnail() might do so, // before the early return below. - surface = orient_thumbnail(surface, orientation); + if (surface) { + int orientation = (intptr_t) cairo_surface_get_user_data( + surface, &fiv_io_key_orientation); + surface = orient_thumbnail(surface, orientation); + } if (!surface || max_size < FIV_THUMBNAIL_SIZE_MIN || max_size > FIV_THUMBNAIL_SIZE_MAX) return surface; |