Refactor fiv_thumbnail_extract()
This commit is contained in:
parent
1e8fe1411b
commit
a5b5e32c3b
224
fiv-thumbnail.c
224
fiv-thumbnail.c
|
@ -240,18 +240,100 @@ orient_thumbnail(cairo_surface_t *surface, FivIoOrientation orientation)
|
||||||
return oriented;
|
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);
|
// Anything else is extremely rare.
|
||||||
if (!path) {
|
if (image->colors != 3 || image->bits != 8) {
|
||||||
set_error(error, "thumbnails will only be extracted from local files");
|
set_error(error, "unsupported bitmap thumbnail");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
GMappedFile *mf = g_mapped_file_new(path, FALSE, error);
|
cairo_surface_t *surface = cairo_image_surface_create(
|
||||||
if (!mf)
|
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;
|
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.:
|
// Bitmap thumbnails generally need rotating, e.g.:
|
||||||
// - Hasselblad/H4D-50/2-9-2017_street_0012.fff
|
// - 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.
|
// Exif Orientation 6, and sizes.flip also contains 6.
|
||||||
// - Nokia/Lumia 1020/RAW_NOKIA_LUMIA_1020.DNG (bitmap) has wrong color.
|
// - Nokia/Lumia 1020/RAW_NOKIA_LUMIA_1020.DNG (bitmap) has wrong color.
|
||||||
// - Ricoh/GXR/R0017428.DNG (JPEG) seems to be plainly invalid.
|
// - 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) {
|
switch (image->type) {
|
||||||
|
gboolean dummy;
|
||||||
case LIBRAW_IMAGE_JPEG:
|
case LIBRAW_IMAGE_JPEG:
|
||||||
surface = render(
|
surface = render(
|
||||||
target, g_bytes_new(image->data, image->data_size), &dummy, error);
|
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;
|
break;
|
||||||
case LIBRAW_IMAGE_BITMAP:
|
case LIBRAW_IMAGE_BITMAP:
|
||||||
// Anything else is extremely rare.
|
surface = extract_libraw_bitmap(iprc, image, error);
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
set_error(error, "unsupported embedded thumbnail");
|
set_error(error, "unsupported embedded thumbnail");
|
||||||
}
|
}
|
||||||
|
|
||||||
libraw_dcraw_clear_mem(image);
|
libraw_dcraw_clear_mem(image);
|
||||||
fail_libraw:
|
fail:
|
||||||
libraw_close(iprc);
|
libraw_close(iprc);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBRAW
|
#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);
|
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.
|
// before the early return below.
|
||||||
|
if (surface) {
|
||||||
|
int orientation = (intptr_t) cairo_surface_get_user_data(
|
||||||
|
surface, &fiv_io_key_orientation);
|
||||||
surface = orient_thumbnail(surface, orientation);
|
surface = orient_thumbnail(surface, orientation);
|
||||||
|
}
|
||||||
if (!surface || max_size < FIV_THUMBNAIL_SIZE_MIN ||
|
if (!surface || max_size < FIV_THUMBNAIL_SIZE_MIN ||
|
||||||
max_size > FIV_THUMBNAIL_SIZE_MAX)
|
max_size > FIV_THUMBNAIL_SIZE_MAX)
|
||||||
return surface;
|
return surface;
|
||||||
|
|
Loading…
Reference in New Issue