Refactor fiv_thumbnail_extract()
This commit is contained in:
parent
1e8fe1411b
commit
a5b5e32c3b
226
fiv-thumbnail.c
226
fiv-thumbnail.c
@ -240,18 +240,100 @@ 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)
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
#if LIBRAW_VERSION >= LIBRAW_MAKE_VERSION(0, 21, 0)
|
||||
if (!iprc->thumbs_list.thumbcount) {
|
||||
set_error(error, "no thumbnails found");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// The old libraw_unpack_thumb() goes for the largest thumbnail,
|
||||
// but we currently want the smallest thumbnail.
|
||||
// TODO(p): To handle the ugly IFD0 thumbnail of NEF,
|
||||
// try to go for the second smallest size. Remember to reflect tflip.
|
||||
int best_index = 0;
|
||||
float best_pixels = INFINITY;
|
||||
for (int i = 0; i < iprc->thumbs_list.thumbcount; i++) {
|
||||
float pixels = (float) iprc->thumbs_list.thumblist[i].twidth *
|
||||
(float) iprc->thumbs_list.thumblist[i].theight;
|
||||
if (pixels && pixels < best_pixels) {
|
||||
best_index = i;
|
||||
best_pixels = pixels;
|
||||
}
|
||||
}
|
||||
if ((err = libraw_unpack_thumb_ex(iprc, best_index))) {
|
||||
set_error(error, libraw_strerror(err));
|
||||
goto fail;
|
||||
}
|
||||
#else // LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0)
|
||||
if ((err = libraw_unpack_thumb(iprc))) {
|
||||
set_error(error, libraw_strerror(err));
|
||||
goto fail;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
// Bitmap thumbnails generally need rotating, e.g.:
|
||||
// - Hasselblad/H4D-50/2-9-2017_street_0012.fff
|
||||
@ -275,114 +357,64 @@ fiv_thumbnail_extract(GFile *target, FivThumbnailSize max_size, GError **error)
|
||||
// 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_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#if LIBRAW_VERSION >= LIBRAW_MAKE_VERSION(0, 21, 0)
|
||||
if (!iprc->thumbs_list.thumbcount) {
|
||||
set_error(error, "no thumbnails found");
|
||||
goto fail_libraw;
|
||||
}
|
||||
|
||||
// The old libraw_unpack_thumb() goes for the largest thumbnail,
|
||||
// but we currently want the smallest thumbnail.
|
||||
// TODO(p): To handle the ugly IFD0 thumbnail of NEF,
|
||||
// try to go for the second smallest size. Remember to reflect tflip.
|
||||
int best_index = 0;
|
||||
float best_pixels = INFINITY;
|
||||
for (int i = 0; i < iprc->thumbs_list.thumbcount; i++) {
|
||||
float pixels = (float) iprc->thumbs_list.thumblist[i].twidth *
|
||||
(float) iprc->thumbs_list.thumblist[i].theight;
|
||||
if (pixels && pixels < best_pixels) {
|
||||
best_index = i;
|
||||
best_pixels = pixels;
|
||||
}
|
||||
}
|
||||
if ((err = libraw_unpack_thumb_ex(iprc, best_index))) {
|
||||
set_error(error, libraw_strerror(err));
|
||||
goto fail_libraw;
|
||||
}
|
||||
#else
|
||||
if ((err = libraw_unpack_thumb(iprc))) {
|
||||
set_error(error, libraw_strerror(err));
|
||||
goto fail_libraw;
|
||||
}
|
||||
#endif
|
||||
|
||||
libraw_processed_image_t *image = libraw_dcraw_make_mem_thumb(iprc, &err);
|
||||
if (!image) {
|
||||
set_error(error, libraw_strerror(err));
|
||||
goto fail_libraw;
|
||||
}
|
||||
|
||||
gboolean dummy = FALSE;
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user