Compare commits
4 Commits
4ca8825e02
...
84f8c9436f
Author | SHA1 | Date | |
---|---|---|---|
84f8c9436f | |||
a8f7532abd | |||
8dfbd0dee2 | |||
930744e165 |
@ -439,6 +439,9 @@ entry_add_thumbnail(gpointer data, gpointer user_data)
|
||||
(intptr_t) cairo_surface_get_user_data(
|
||||
cached, &fiv_browser_key_mtime_msec) == self->mtime_msec) {
|
||||
self->thumbnail = cairo_surface_reference(cached);
|
||||
// TODO(p): If this hit is low-quality, see if a high-quality thumbnail
|
||||
// hasn't been produced without our knowledge (avoid launching a minion
|
||||
// unnecessarily; we might also shift the concern there).
|
||||
} else {
|
||||
cairo_surface_t *found = fiv_thumbnail_lookup(
|
||||
self->uri, self->mtime_msec, browser->item_size);
|
||||
@ -555,18 +558,31 @@ thumbnailer_reprocess_entry(FivBrowser *self, GBytes *output, Entry *entry)
|
||||
{
|
||||
g_clear_object(&entry->icon);
|
||||
g_clear_pointer(&entry->thumbnail, cairo_surface_destroy);
|
||||
if (!output || !(entry->thumbnail = rescale_thumbnail(
|
||||
fiv_io_deserialize(output), self->item_height))) {
|
||||
entry_add_thumbnail(entry, self);
|
||||
materialize_icon(self, entry);
|
||||
} else {
|
||||
// This choice of mtime favours unnecessary thumbnail reloading.
|
||||
cairo_surface_set_user_data(entry->thumbnail,
|
||||
&fiv_browser_key_mtime_msec, (void *) (intptr_t) entry->mtime_msec,
|
||||
NULL);
|
||||
}
|
||||
|
||||
gtk_widget_queue_resize(GTK_WIDGET(self));
|
||||
|
||||
guint64 flags = 0;
|
||||
if (!output || !(entry->thumbnail = rescale_thumbnail(
|
||||
fiv_io_deserialize(output, &flags), self->item_height))) {
|
||||
entry_add_thumbnail(entry, self);
|
||||
materialize_icon(self, entry);
|
||||
return;
|
||||
}
|
||||
if ((flags & FIV_IO_SERIALIZE_LOW_QUALITY)) {
|
||||
cairo_surface_set_user_data(entry->thumbnail, &fiv_thumbnail_key_lq,
|
||||
(void *) (intptr_t) 1, NULL);
|
||||
|
||||
// TODO(p): Improve complexity; this will iterate the whole linked list.
|
||||
self->thumbnailers_queue =
|
||||
g_list_append(self->thumbnailers_queue, entry);
|
||||
}
|
||||
|
||||
// This choice of mtime favours unnecessary thumbnail reloading.
|
||||
cairo_surface_set_user_data(entry->thumbnail,
|
||||
&fiv_browser_key_mtime_msec, (void *) (intptr_t) entry->mtime_msec,
|
||||
NULL);
|
||||
g_hash_table_insert(self->thumbnail_cache, g_strdup(entry->uri),
|
||||
cairo_surface_reference(entry->thumbnail));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -628,11 +644,23 @@ thumbnailer_next(Thumbnailer *t)
|
||||
self->thumbnailers_queue =
|
||||
g_list_delete_link(self->thumbnailers_queue, self->thumbnailers_queue);
|
||||
|
||||
// Case analysis:
|
||||
// - We haven't found any thumbnail for the entry at all
|
||||
// (and it has a symbolic icon as a result):
|
||||
// we want to fill the void ASAP, so go for embedded thumbnails first.
|
||||
// - We've found one, but we're not quite happy with it:
|
||||
// always run the full process for a high-quality wide thumbnail.
|
||||
// - We can't end up here in any other cases.
|
||||
const char *argv_faster[] = {PROJECT_NAME, "--extract-thumbnail",
|
||||
"--thumbnail", fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name,
|
||||
"--", t->target->uri, NULL};
|
||||
const char *argv_slower[] = {PROJECT_NAME,
|
||||
"--thumbnail", fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name,
|
||||
"--", t->target->uri, NULL};
|
||||
|
||||
GError *error = NULL;
|
||||
t->minion = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error,
|
||||
PROJECT_NAME, "--thumbnail",
|
||||
fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name, "--",
|
||||
t->target->uri, NULL);
|
||||
t->minion = g_subprocess_newv(t->target->icon ? argv_faster : argv_slower,
|
||||
G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error);
|
||||
if (error) {
|
||||
g_warning("%s", error->message);
|
||||
g_error_free(error);
|
||||
|
7
fiv-io.c
7
fiv-io.c
@ -2891,11 +2891,12 @@ fiv_io_open_from_data(
|
||||
// --- Thumbnail passing utilities ---------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
guint64 user_data;
|
||||
int width, height, stride, format;
|
||||
} CairoHeader;
|
||||
|
||||
void
|
||||
fiv_io_serialize_to_stdout(cairo_surface_t *surface)
|
||||
fiv_io_serialize_to_stdout(cairo_surface_t *surface, guint64 user_data)
|
||||
{
|
||||
if (!surface || cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE)
|
||||
return;
|
||||
@ -2907,6 +2908,7 @@ fiv_io_serialize_to_stdout(cairo_surface_t *surface)
|
||||
#endif
|
||||
|
||||
CairoHeader h = {
|
||||
.user_data = user_data,
|
||||
.width = cairo_image_surface_get_width(surface),
|
||||
.height = cairo_image_surface_get_height(surface),
|
||||
.stride = cairo_image_surface_get_stride(surface),
|
||||
@ -2921,7 +2923,7 @@ fiv_io_serialize_to_stdout(cairo_surface_t *surface)
|
||||
}
|
||||
|
||||
cairo_surface_t *
|
||||
fiv_io_deserialize(GBytes *bytes)
|
||||
fiv_io_deserialize(GBytes *bytes, guint64 *user_data)
|
||||
{
|
||||
CairoHeader h = {};
|
||||
GByteArray *array = g_bytes_unref_to_array(bytes);
|
||||
@ -2949,6 +2951,7 @@ fiv_io_deserialize(GBytes *bytes)
|
||||
static cairo_user_data_key_t key;
|
||||
cairo_surface_set_user_data(
|
||||
surface, &key, array, (cairo_destroy_func_t) g_byte_array_unref);
|
||||
*user_data = h.user_data;
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
6
fiv-io.h
6
fiv-io.h
@ -102,8 +102,10 @@ cairo_surface_t *fiv_io_open_png_thumbnail(const char *path, GError **error);
|
||||
|
||||
// --- Thumbnail passing utilities ---------------------------------------------
|
||||
|
||||
void fiv_io_serialize_to_stdout(cairo_surface_t *surface);
|
||||
cairo_surface_t *fiv_io_deserialize(GBytes *bytes);
|
||||
enum { FIV_IO_SERIALIZE_LOW_QUALITY = 1 << 0 };
|
||||
|
||||
void fiv_io_serialize_to_stdout(cairo_surface_t *surface, guint64 user_data);
|
||||
cairo_surface_t *fiv_io_deserialize(GBytes *bytes, guint64 *user_data);
|
||||
|
||||
// --- Filesystem --------------------------------------------------------------
|
||||
|
||||
|
159
fiv-thumbnail.c
159
fiv-thumbnail.c
@ -30,6 +30,10 @@
|
||||
#include "fiv-thumbnail.h"
|
||||
#include "xdg.h"
|
||||
|
||||
#ifdef HAVE_LIBRAW
|
||||
#include <libraw.h>
|
||||
#endif // HAVE_LIBRAW
|
||||
|
||||
#ifndef __linux__
|
||||
#define st_mtim st_mtimespec
|
||||
#endif // ! __linux__
|
||||
@ -104,6 +108,28 @@ 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;
|
||||
}
|
||||
|
||||
// In principle similar to rescale_thumbnail() from fiv-browser.c.
|
||||
static cairo_surface_t *
|
||||
adjust_thumbnail(cairo_surface_t *thumbnail, double row_height)
|
||||
@ -175,6 +201,76 @@ adjust_thumbnail(cairo_surface_t *thumbnail, double row_height)
|
||||
return scaled;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
if (!surface || max_size < FIV_THUMBNAIL_SIZE_MIN ||
|
||||
max_size > FIV_THUMBNAIL_SIZE_MAX)
|
||||
return surface;
|
||||
|
||||
cairo_surface_t *result =
|
||||
adjust_thumbnail(surface, fiv_thumbnail_sizes[max_size].size);
|
||||
cairo_surface_destroy(surface);
|
||||
return result;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static WebPData
|
||||
encode_thumbnail(cairo_surface_t *surface)
|
||||
{
|
||||
@ -238,30 +334,7 @@ save_thumbnail(cairo_surface_t *thumbnail, const char *path, GString *thum)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
produce_fallback(GFile *target, FivThumbnailSize size,
|
||||
cairo_surface_t **surface, GError **error)
|
||||
produce_fallback(GFile *target, FivThumbnailSize size, GError **error)
|
||||
{
|
||||
goffset filesize = 0;
|
||||
GFileInfo *info = g_file_query_info(target,
|
||||
@ -276,40 +349,39 @@ produce_fallback(GFile *target, FivThumbnailSize size,
|
||||
// For example, we can employ magic checks.
|
||||
if (filesize > 10 << 20) {
|
||||
set_error(error, "oversize, not thumbnailing");
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GBytes *data = g_file_load_bytes(target, NULL, NULL, error);
|
||||
if (!data)
|
||||
return FALSE;
|
||||
return NULL;
|
||||
|
||||
gboolean color_managed = FALSE;
|
||||
cairo_surface_t *result = render(target, data, &color_managed, error);
|
||||
if (!result)
|
||||
return FALSE;
|
||||
cairo_surface_t *surface = render(target, data, &color_managed, error);
|
||||
if (!surface)
|
||||
return NULL;
|
||||
|
||||
if (!*surface)
|
||||
*surface = adjust_thumbnail(result, fiv_thumbnail_sizes[size].size);
|
||||
cairo_surface_destroy(result);
|
||||
return TRUE;
|
||||
cairo_surface_t *result =
|
||||
adjust_thumbnail(surface, fiv_thumbnail_sizes[size].size);
|
||||
cairo_surface_destroy(surface);
|
||||
return result;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
|
||||
cairo_surface_t **max_size_surface, GError **error)
|
||||
cairo_surface_t *
|
||||
fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size, GError **error)
|
||||
{
|
||||
g_return_val_if_fail(max_size >= FIV_THUMBNAIL_SIZE_MIN &&
|
||||
max_size <= FIV_THUMBNAIL_SIZE_MAX, FALSE);
|
||||
|
||||
const gchar *path = g_file_peek_path(target);
|
||||
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.
|
||||
GStatBuf st = {};
|
||||
if (g_stat(path, &st)) {
|
||||
set_error(error, g_strerror(errno));
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GError *e = NULL;
|
||||
@ -317,7 +389,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
|
||||
if (!mf) {
|
||||
g_debug("%s: %s", path, e->message);
|
||||
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);
|
||||
@ -326,7 +398,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
|
||||
render(target, g_mapped_file_get_bytes(mf), &color_managed, error);
|
||||
g_mapped_file_unref(mf);
|
||||
if (!surface)
|
||||
return FALSE;
|
||||
return NULL;
|
||||
|
||||
// Boilerplate copied from fiv_thumbnail_lookup().
|
||||
gchar *uri = g_file_get_uri(target);
|
||||
@ -354,6 +426,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
|
||||
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--) {
|
||||
cairo_surface_t *scaled =
|
||||
adjust_thumbnail(surface, fiv_thumbnail_sizes[use].size);
|
||||
@ -362,8 +435,8 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
|
||||
save_thumbnail(scaled, path, thum);
|
||||
g_free(path);
|
||||
|
||||
if (!*max_size_surface)
|
||||
*max_size_surface = scaled;
|
||||
if (!max_size_surface)
|
||||
max_size_surface = scaled;
|
||||
else
|
||||
cairo_surface_destroy(scaled);
|
||||
}
|
||||
@ -374,7 +447,7 @@ fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
|
||||
g_free(sum);
|
||||
g_free(uri);
|
||||
cairo_surface_destroy(surface);
|
||||
return TRUE;
|
||||
return max_size_surface;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -45,20 +45,25 @@ typedef struct _FivThumbnailSizeInfo {
|
||||
const char *thumbnail_spec_name; ///< thumbnail-spec directory name
|
||||
} FivThumbnailSizeInfo;
|
||||
|
||||
extern FivThumbnailSizeInfo fiv_thumbnail_sizes[FIV_THUMBNAIL_SIZE_COUNT];
|
||||
|
||||
enum { FIV_THUMBNAIL_WIDE_COEFFICIENT = 2 };
|
||||
|
||||
extern FivThumbnailSizeInfo fiv_thumbnail_sizes[FIV_THUMBNAIL_SIZE_COUNT];
|
||||
|
||||
/// If non-NULL, indicates a thumbnail of insufficient quality.
|
||||
extern cairo_user_data_key_t fiv_thumbnail_key_lq;
|
||||
|
||||
/// Returns this user's root thumbnail directory.
|
||||
gchar *fiv_thumbnail_get_root(void);
|
||||
|
||||
/// Attempts to extract any low-quality thumbnail from fast targets.
|
||||
/// If `max_size` is a valid value, the image will be downscaled as appropriate.
|
||||
cairo_surface_t *fiv_thumbnail_extract(
|
||||
GFile *target, FivThumbnailSize max_size, GError **error);
|
||||
|
||||
/// 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).
|
||||
gboolean fiv_thumbnail_produce(GFile *target, FivThumbnailSize max_size,
|
||||
cairo_surface_t **max_size_surface, GError **error);
|
||||
/// Returns the surface used for the maximum size, or an error.
|
||||
cairo_surface_t *fiv_thumbnail_produce(
|
||||
GFile *target, FivThumbnailSize max_size, GError **error);
|
||||
|
||||
/// Retrieves a thumbnail of the most appropriate quality and resolution
|
||||
/// 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; }";
|
||||
|
||||
static void
|
||||
output_thumbnail(const char *path_arg, gboolean extract, const char *size_arg)
|
||||
{
|
||||
if (!path_arg)
|
||||
exit_fatal("no path given");
|
||||
|
||||
FivThumbnailSize size = FIV_THUMBNAIL_SIZE_COUNT;
|
||||
if (size_arg) {
|
||||
for (size = 0; 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, size, &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
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
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;
|
||||
const GOptionEntry options[] = {
|
||||
{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,
|
||||
G_OPTION_ARG_NONE, &browse,
|
||||
"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,
|
||||
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,
|
||||
G_OPTION_ARG_NONE, &invalidate_cache,
|
||||
"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.
|
||||
// Best show the help message, if we can figure that out.
|
||||
const gchar *path_arg = path_args ? path_args[0] : NULL;
|
||||
if (thumbnail_size) {
|
||||
if (!path_arg)
|
||||
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);
|
||||
}
|
||||
if (extract_thumbnail || thumbnail_size) {
|
||||
output_thumbnail(path_arg, extract_thumbnail, thumbnail_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user