diff options
Diffstat (limited to 'fiv-thumbnail.c')
| -rw-r--r-- | fiv-thumbnail.c | 182 | 
1 files changed, 107 insertions, 75 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; +	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); -	// 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; +	// 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;  | 
