Add a command line option to extract thumbnails
Only use LibRaw for now, which probably has the most impact using the least amount of effort.
This commit is contained in:
		@@ -555,8 +555,9 @@ thumbnailer_reprocess_entry(FivBrowser *self, GBytes *output, Entry *entry)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	g_clear_object(&entry->icon);
 | 
						g_clear_object(&entry->icon);
 | 
				
			||||||
	g_clear_pointer(&entry->thumbnail, cairo_surface_destroy);
 | 
						g_clear_pointer(&entry->thumbnail, cairo_surface_destroy);
 | 
				
			||||||
 | 
						guint64 dummy;
 | 
				
			||||||
	if (!output || !(entry->thumbnail = rescale_thumbnail(
 | 
						if (!output || !(entry->thumbnail = rescale_thumbnail(
 | 
				
			||||||
			fiv_io_deserialize(output), self->item_height))) {
 | 
								fiv_io_deserialize(output, &dummy), self->item_height))) {
 | 
				
			||||||
		entry_add_thumbnail(entry, self);
 | 
							entry_add_thumbnail(entry, self);
 | 
				
			||||||
		materialize_icon(self, entry);
 | 
							materialize_icon(self, entry);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										152
									
								
								fiv-thumbnail.c
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								fiv-thumbnail.c
									
									
									
									
									
								
							@@ -30,6 +30,10 @@
 | 
				
			|||||||
#include "fiv-thumbnail.h"
 | 
					#include "fiv-thumbnail.h"
 | 
				
			||||||
#include "xdg.h"
 | 
					#include "xdg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_LIBRAW
 | 
				
			||||||
 | 
					#include <libraw.h>
 | 
				
			||||||
 | 
					#endif  // HAVE_LIBRAW
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef __linux__
 | 
					#ifndef __linux__
 | 
				
			||||||
#define st_mtim st_mtimespec
 | 
					#define st_mtim st_mtimespec
 | 
				
			||||||
#endif  // ! __linux__
 | 
					#endif  // ! __linux__
 | 
				
			||||||
@@ -104,6 +108,91 @@ fiv_thumbnail_get_root(void)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static cairo_surface_t *
 | 
				
			||||||
 | 
					render(GFile *target, GBytes *data, gboolean *color_managed, GError **error)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						FivIoOpenContext ctx = {
 | 
				
			||||||
 | 
							.uri = g_file_get_uri(target),
 | 
				
			||||||
 | 
							.screen_profile = fiv_io_profile_new_sRGB(),
 | 
				
			||||||
 | 
							.screen_dpi = 96,
 | 
				
			||||||
 | 
							.first_frame_only = TRUE,
 | 
				
			||||||
 | 
							// Only using this array as a redirect.
 | 
				
			||||||
 | 
							.warnings = g_ptr_array_new_with_free_func(g_free),
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cairo_surface_t *surface = fiv_io_open_from_data(
 | 
				
			||||||
 | 
							g_bytes_get_data(data, NULL), g_bytes_get_size(data), &ctx, error);
 | 
				
			||||||
 | 
						g_free((gchar *) ctx.uri);
 | 
				
			||||||
 | 
						g_ptr_array_free(ctx.warnings, TRUE);
 | 
				
			||||||
 | 
						if ((*color_managed = !!ctx.screen_profile))
 | 
				
			||||||
 | 
							fiv_io_profile_free(ctx.screen_profile);
 | 
				
			||||||
 | 
						g_bytes_unref(data);
 | 
				
			||||||
 | 
						return surface;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cairo_surface_t *
 | 
				
			||||||
 | 
					fiv_thumbnail_extract(GFile *target, 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cairo_surface_t *surface = NULL;
 | 
				
			||||||
 | 
					#ifndef HAVE_LIBRAW
 | 
				
			||||||
 | 
						// TODO(p): Implement our own thumbnail extractors.
 | 
				
			||||||
 | 
						set_error(error, "unsupported file");
 | 
				
			||||||
 | 
					#else  // HAVE_LIBRAW
 | 
				
			||||||
 | 
						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))) ||
 | 
				
			||||||
 | 
							(err = libraw_unpack_thumb(iprc))) {
 | 
				
			||||||
 | 
							set_error(error, libraw_strerror(err));
 | 
				
			||||||
 | 
							goto fail_libraw;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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) {
 | 
				
			||||||
 | 
						case LIBRAW_IMAGE_JPEG:
 | 
				
			||||||
 | 
							surface = render(
 | 
				
			||||||
 | 
								target, g_bytes_new(image->data, image->data_size), &dummy, error);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case LIBRAW_IMAGE_BITMAP:
 | 
				
			||||||
 | 
							// TODO(p): Implement this one as well.
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							set_error(error, "unsupported embedded thumbnail");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						libraw_dcraw_clear_mem(image);
 | 
				
			||||||
 | 
					fail_libraw:
 | 
				
			||||||
 | 
						libraw_close(iprc);
 | 
				
			||||||
 | 
					#endif  // HAVE_LIBRAW
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fail:
 | 
				
			||||||
 | 
						g_mapped_file_unref(mf);
 | 
				
			||||||
 | 
						return surface;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// In principle similar to rescale_thumbnail() from fiv-browser.c.
 | 
					// In principle similar to rescale_thumbnail() from fiv-browser.c.
 | 
				
			||||||
static cairo_surface_t *
 | 
					static cairo_surface_t *
 | 
				
			||||||
adjust_thumbnail(cairo_surface_t *thumbnail, double row_height)
 | 
					adjust_thumbnail(cairo_surface_t *thumbnail, double row_height)
 | 
				
			||||||
@@ -238,30 +327,7 @@ save_thumbnail(cairo_surface_t *thumbnail, const char *path, GString *thum)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static cairo_surface_t *
 | 
					static cairo_surface_t *
 | 
				
			||||||
render(GFile *target, GBytes *data, gboolean *color_managed, GError **error)
 | 
					produce_fallback(GFile *target, FivThumbnailSize size, GError **error)
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	FivIoOpenContext ctx = {
 | 
					 | 
				
			||||||
		.uri = g_file_get_uri(target),
 | 
					 | 
				
			||||||
		.screen_profile = fiv_io_profile_new_sRGB(),
 | 
					 | 
				
			||||||
		.screen_dpi = 96,
 | 
					 | 
				
			||||||
		.first_frame_only = TRUE,
 | 
					 | 
				
			||||||
		// Only using this array as a redirect.
 | 
					 | 
				
			||||||
		.warnings = g_ptr_array_new_with_free_func(g_free),
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cairo_surface_t *surface = fiv_io_open_from_data(
 | 
					 | 
				
			||||||
		g_bytes_get_data(data, NULL), g_bytes_get_size(data), &ctx, error);
 | 
					 | 
				
			||||||
	g_free((gchar *) ctx.uri);
 | 
					 | 
				
			||||||
	g_ptr_array_free(ctx.warnings, TRUE);
 | 
					 | 
				
			||||||
	if ((*color_managed = !!ctx.screen_profile))
 | 
					 | 
				
			||||||
		fiv_io_profile_free(ctx.screen_profile);
 | 
					 | 
				
			||||||
	g_bytes_unref(data);
 | 
					 | 
				
			||||||
	return surface;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gboolean
 | 
					 | 
				
			||||||
produce_fallback(GFile *target, FivThumbnailSize size,
 | 
					 | 
				
			||||||
	cairo_surface_t **surface, GError **error)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	goffset filesize = 0;
 | 
						goffset filesize = 0;
 | 
				
			||||||
	GFileInfo *info = g_file_query_info(target,
 | 
						GFileInfo *info = g_file_query_info(target,
 | 
				
			||||||
@@ -276,40 +342,39 @@ produce_fallback(GFile *target, FivThumbnailSize size,
 | 
				
			|||||||
	// For example, we can employ magic checks.
 | 
						// For example, we can employ magic checks.
 | 
				
			||||||
	if (filesize > 10 << 20) {
 | 
						if (filesize > 10 << 20) {
 | 
				
			||||||
		set_error(error, "oversize, not thumbnailing");
 | 
							set_error(error, "oversize, not thumbnailing");
 | 
				
			||||||
		return FALSE;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GBytes *data = g_file_load_bytes(target, NULL, NULL, error);
 | 
						GBytes *data = g_file_load_bytes(target, NULL, NULL, error);
 | 
				
			||||||
	if (!data)
 | 
						if (!data)
 | 
				
			||||||
		return FALSE;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gboolean color_managed = FALSE;
 | 
						gboolean color_managed = FALSE;
 | 
				
			||||||
	cairo_surface_t *result = render(target, data, &color_managed, error);
 | 
						cairo_surface_t *surface = render(target, data, &color_managed, error);
 | 
				
			||||||
	if (!result)
 | 
						if (!surface)
 | 
				
			||||||
		return FALSE;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!*surface)
 | 
						cairo_surface_t *result =
 | 
				
			||||||
		*surface = adjust_thumbnail(result, fiv_thumbnail_sizes[size].size);
 | 
							adjust_thumbnail(surface, fiv_thumbnail_sizes[size].size);
 | 
				
			||||||
	cairo_surface_destroy(result);
 | 
						cairo_surface_destroy(surface);
 | 
				
			||||||
	return TRUE;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gboolean
 | 
					cairo_surface_t *
 | 
				
			||||||
fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
 | 
					fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error)
 | 
				
			||||||
	cairo_surface_t **max_size_surface, GError **error)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	g_return_val_if_fail(max_size >= FIV_THUMBNAIL_SIZE_MIN &&
 | 
						g_return_val_if_fail(max_size >= FIV_THUMBNAIL_SIZE_MIN &&
 | 
				
			||||||
		max_size <= FIV_THUMBNAIL_SIZE_MAX, FALSE);
 | 
							max_size <= FIV_THUMBNAIL_SIZE_MAX, FALSE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const gchar *path = g_file_peek_path(target);
 | 
						const gchar *path = g_file_peek_path(target);
 | 
				
			||||||
	if (!path || !g_file_is_native(target) /* Don't save sftp://. */)
 | 
						if (!path || !g_file_is_native(target) /* Don't save sftp://. */)
 | 
				
			||||||
		return produce_fallback(target, max_size, max_size_surface, error);
 | 
							return produce_fallback(target, max_size, error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Make the TOCTTOU issue favour unnecessary reloading.
 | 
						// Make the TOCTTOU issue favour unnecessary reloading.
 | 
				
			||||||
	GStatBuf st = {};
 | 
						GStatBuf st = {};
 | 
				
			||||||
	if (g_stat(path, &st)) {
 | 
						if (g_stat(path, &st)) {
 | 
				
			||||||
		set_error(error, g_strerror(errno));
 | 
							set_error(error, g_strerror(errno));
 | 
				
			||||||
		return FALSE;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GError *e = NULL;
 | 
						GError *e = NULL;
 | 
				
			||||||
@@ -317,7 +382,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
 | 
				
			|||||||
	if (!mf) {
 | 
						if (!mf) {
 | 
				
			||||||
		g_debug("%s: %s", path, e->message);
 | 
							g_debug("%s: %s", path, e->message);
 | 
				
			||||||
		g_error_free(e);
 | 
							g_error_free(e);
 | 
				
			||||||
		return produce_fallback(target, max_size, max_size_surface, error);
 | 
							return produce_fallback(target, max_size, error);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gsize filesize = g_mapped_file_get_length(mf);
 | 
						gsize filesize = g_mapped_file_get_length(mf);
 | 
				
			||||||
@@ -326,7 +391,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
 | 
				
			|||||||
		render(target, g_mapped_file_get_bytes(mf), &color_managed, error);
 | 
							render(target, g_mapped_file_get_bytes(mf), &color_managed, error);
 | 
				
			||||||
	g_mapped_file_unref(mf);
 | 
						g_mapped_file_unref(mf);
 | 
				
			||||||
	if (!surface)
 | 
						if (!surface)
 | 
				
			||||||
		return FALSE;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Boilerplate copied from fiv_thumbnail_lookup().
 | 
						// Boilerplate copied from fiv_thumbnail_lookup().
 | 
				
			||||||
	gchar *uri = g_file_get_uri(target);
 | 
						gchar *uri = g_file_get_uri(target);
 | 
				
			||||||
@@ -354,6 +419,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
 | 
				
			|||||||
			thum, "%s%c%s%c", THUMB_COLORSPACE, 0, THUMB_COLORSPACE_SRGB, 0);
 | 
								thum, "%s%c%s%c", THUMB_COLORSPACE, 0, THUMB_COLORSPACE_SRGB, 0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cairo_surface_t *max_size_surface = NULL;
 | 
				
			||||||
	for (int use = max_size; use >= FIV_THUMBNAIL_SIZE_MIN; use--) {
 | 
						for (int use = max_size; use >= FIV_THUMBNAIL_SIZE_MIN; use--) {
 | 
				
			||||||
		cairo_surface_t *scaled =
 | 
							cairo_surface_t *scaled =
 | 
				
			||||||
			adjust_thumbnail(surface, fiv_thumbnail_sizes[use].size);
 | 
								adjust_thumbnail(surface, fiv_thumbnail_sizes[use].size);
 | 
				
			||||||
@@ -362,8 +428,8 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
 | 
				
			|||||||
		save_thumbnail(scaled, path, thum);
 | 
							save_thumbnail(scaled, path, thum);
 | 
				
			||||||
		g_free(path);
 | 
							g_free(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!*max_size_surface)
 | 
							if (!max_size_surface)
 | 
				
			||||||
			*max_size_surface = scaled;
 | 
								max_size_surface = scaled;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			cairo_surface_destroy(scaled);
 | 
								cairo_surface_destroy(scaled);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -374,7 +440,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
 | 
				
			|||||||
	g_free(sum);
 | 
						g_free(sum);
 | 
				
			||||||
	g_free(uri);
 | 
						g_free(uri);
 | 
				
			||||||
	cairo_surface_destroy(surface);
 | 
						cairo_surface_destroy(surface);
 | 
				
			||||||
	return TRUE;
 | 
						return max_size_surface;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,10 +55,13 @@ extern cairo_user_data_key_t fiv_thumbnail_key_lq;
 | 
				
			|||||||
/// Returns this user's root thumbnail directory.
 | 
					/// Returns this user's root thumbnail directory.
 | 
				
			||||||
gchar *fiv_thumbnail_get_root(void);
 | 
					gchar *fiv_thumbnail_get_root(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Attempts to extract any low-quality thumbnail from fast targets.
 | 
				
			||||||
 | 
					cairo_surface_t *fiv_thumbnail_extract(GFile *target, GError **error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Generates wide thumbnails of up to the specified size, saves them in cache.
 | 
					/// Generates wide thumbnails of up to the specified size, saves them in cache.
 | 
				
			||||||
/// Returns the surface used for the maximum size (if the pointer was NULL).
 | 
					/// Returns the surface used for the maximum size, or an error.
 | 
				
			||||||
gboolean fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
 | 
					cairo_surface_t *fiv_thumbnail_produce(
 | 
				
			||||||
	cairo_surface_t **max_size_surface, GError **error);
 | 
						GFile *target, FivThumbnailSize max_size, GError **error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Retrieves a thumbnail of the most appropriate quality and resolution
 | 
					/// Retrieves a thumbnail of the most appropriate quality and resolution
 | 
				
			||||||
/// for the target file.
 | 
					/// for the target file.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										66
									
								
								fiv.c
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								fiv.c
									
									
									
									
									
								
							@@ -1767,11 +1767,47 @@ static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \
 | 
				
			|||||||
	} \
 | 
						} \
 | 
				
			||||||
	.fiv-information label { padding: 0 4px; }";
 | 
						.fiv-information label { padding: 0 4px; }";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					output_thumbnail(const char *path_arg, gboolean extract, const char *size_arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!path_arg)
 | 
				
			||||||
 | 
							exit_fatal("no path given");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						FivThumbnailSize size = 0;
 | 
				
			||||||
 | 
						if (size_arg) {
 | 
				
			||||||
 | 
							for (; size < FIV_THUMBNAIL_SIZE_COUNT; size++) {
 | 
				
			||||||
 | 
								if (!strcmp(
 | 
				
			||||||
 | 
										fiv_thumbnail_sizes[size].thumbnail_spec_name, size_arg))
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (size >= FIV_THUMBNAIL_SIZE_COUNT)
 | 
				
			||||||
 | 
								exit_fatal("unknown thumbnail size: %s", size_arg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GError *error = NULL;
 | 
				
			||||||
 | 
						GFile *file = g_file_new_for_commandline_arg(path_arg);
 | 
				
			||||||
 | 
						cairo_surface_t *surface = NULL;
 | 
				
			||||||
 | 
						if (extract && (surface = fiv_thumbnail_extract(file, &error)))
 | 
				
			||||||
 | 
							fiv_io_serialize_to_stdout(surface, FIV_IO_SERIALIZE_LOW_QUALITY);
 | 
				
			||||||
 | 
						else if (size_arg &&
 | 
				
			||||||
 | 
							(g_clear_error(&error),
 | 
				
			||||||
 | 
								(surface = fiv_thumbnail_produce(file, size, &error))))
 | 
				
			||||||
 | 
							fiv_io_serialize_to_stdout(surface, 0);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							g_assert(error != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g_object_unref(file);
 | 
				
			||||||
 | 
						if (error)
 | 
				
			||||||
 | 
							exit_fatal("%s", error->message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cairo_surface_destroy(surface);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
main(int argc, char *argv[])
 | 
					main(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	gboolean show_version = FALSE, show_supported_media_types = FALSE,
 | 
						gboolean show_version = FALSE, show_supported_media_types = FALSE,
 | 
				
			||||||
		invalidate_cache = FALSE, browse = FALSE;
 | 
							invalidate_cache = FALSE, browse = FALSE, extract_thumbnail = FALSE;
 | 
				
			||||||
	gchar **path_args = NULL, *thumbnail_size = NULL;
 | 
						gchar **path_args = NULL, *thumbnail_size = NULL;
 | 
				
			||||||
	const GOptionEntry options[] = {
 | 
						const GOptionEntry options[] = {
 | 
				
			||||||
		{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &path_args,
 | 
							{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &path_args,
 | 
				
			||||||
@@ -1782,9 +1818,12 @@ main(int argc, char *argv[])
 | 
				
			|||||||
		{"browse", 0, G_OPTION_FLAG_IN_MAIN,
 | 
							{"browse", 0, G_OPTION_FLAG_IN_MAIN,
 | 
				
			||||||
			G_OPTION_ARG_NONE, &browse,
 | 
								G_OPTION_ARG_NONE, &browse,
 | 
				
			||||||
			"Start in filesystem browsing mode", NULL},
 | 
								"Start in filesystem browsing mode", NULL},
 | 
				
			||||||
 | 
							{"extract-thumbnail", 0, G_OPTION_FLAG_IN_MAIN,
 | 
				
			||||||
 | 
								G_OPTION_ARG_NONE, &extract_thumbnail,
 | 
				
			||||||
 | 
								"Output any embedded thumbnail (superseding --thumbnail)", NULL},
 | 
				
			||||||
		{"thumbnail", 0, G_OPTION_FLAG_IN_MAIN,
 | 
							{"thumbnail", 0, G_OPTION_FLAG_IN_MAIN,
 | 
				
			||||||
			G_OPTION_ARG_STRING, &thumbnail_size,
 | 
								G_OPTION_ARG_STRING, &thumbnail_size,
 | 
				
			||||||
			"Generate thumbnails for an image, up to the given size", "SIZE"},
 | 
								"Generate thumbnails, up to SIZE, and output that size", "SIZE"},
 | 
				
			||||||
		{"invalidate-cache", 0, G_OPTION_FLAG_IN_MAIN,
 | 
							{"invalidate-cache", 0, G_OPTION_FLAG_IN_MAIN,
 | 
				
			||||||
			G_OPTION_ARG_NONE, &invalidate_cache,
 | 
								G_OPTION_ARG_NONE, &invalidate_cache,
 | 
				
			||||||
			"Invalidate the wide thumbnail cache", NULL},
 | 
								"Invalidate the wide thumbnail cache", NULL},
 | 
				
			||||||
@@ -1820,27 +1859,8 @@ main(int argc, char *argv[])
 | 
				
			|||||||
	// TODO(p): Complain to the user if there's more than one argument.
 | 
						// TODO(p): Complain to the user if there's more than one argument.
 | 
				
			||||||
	// Best show the help message, if we can figure that out.
 | 
						// Best show the help message, if we can figure that out.
 | 
				
			||||||
	const gchar *path_arg = path_args ? path_args[0] : NULL;
 | 
						const gchar *path_arg = path_args ? path_args[0] : NULL;
 | 
				
			||||||
	if (thumbnail_size) {
 | 
						if (extract_thumbnail || thumbnail_size) {
 | 
				
			||||||
		if (!path_arg)
 | 
							output_thumbnail(path_arg, extract_thumbnail, thumbnail_size);
 | 
				
			||||||
			exit_fatal("no path given");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		FivThumbnailSize size = 0;
 | 
					 | 
				
			||||||
		for (; size < FIV_THUMBNAIL_SIZE_COUNT; size++)
 | 
					 | 
				
			||||||
			if (!strcmp(fiv_thumbnail_sizes[size].thumbnail_spec_name,
 | 
					 | 
				
			||||||
					thumbnail_size))
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
		if (size >= FIV_THUMBNAIL_SIZE_COUNT)
 | 
					 | 
				
			||||||
			exit_fatal("unknown thumbnail size: %s", thumbnail_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		GFile *target = g_file_new_for_commandline_arg(path_arg);
 | 
					 | 
				
			||||||
		cairo_surface_t *surface = NULL;
 | 
					 | 
				
			||||||
		if (!fiv_thumbnail_produce(target, size, &surface, &error))
 | 
					 | 
				
			||||||
			exit_fatal("%s", error->message);
 | 
					 | 
				
			||||||
		g_object_unref(target);
 | 
					 | 
				
			||||||
		if (surface) {
 | 
					 | 
				
			||||||
			fiv_io_serialize_to_stdout(surface);
 | 
					 | 
				
			||||||
			cairo_surface_destroy(surface);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user