From a8f7532abdd9eade6ccc7587968e63eea3b5d159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Wed, 8 Jun 2022 01:05:04 +0200 Subject: [PATCH] Employ embedded thumbnail extraction And store all direct thumbnailer output in the browser's cache-- low-quality thumbnails will always be regenerated, as is desired, and we'll reload faster on devices where we don't store thumbnails. This change improves latency at the cost of overall efficiency, seeing as images with thumbnails will be spent cycles on twice. Keeping this out-of-process avoids undesired lock-ups. Moreover, embedded thumbnails can be fairly expensive to decode. --- fiv-browser.c | 57 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/fiv-browser.c b/fiv-browser.c index 111c291..10cb17a 100644 --- a/fiv-browser.c +++ b/fiv-browser.c @@ -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,19 +558,31 @@ thumbnailer_reprocess_entry(FivBrowser *self, GBytes *output, Entry *entry) { g_clear_object(&entry->icon); g_clear_pointer(&entry->thumbnail, cairo_surface_destroy); - guint64 dummy; - if (!output || !(entry->thumbnail = rescale_thumbnail( - fiv_io_deserialize(output, &dummy), 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 @@ -629,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);