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.
This commit is contained in:
parent
8dfbd0dee2
commit
a8f7532abd
|
@ -439,6 +439,9 @@ entry_add_thumbnail(gpointer data, gpointer user_data)
|
||||||
(intptr_t) cairo_surface_get_user_data(
|
(intptr_t) cairo_surface_get_user_data(
|
||||||
cached, &fiv_browser_key_mtime_msec) == self->mtime_msec) {
|
cached, &fiv_browser_key_mtime_msec) == self->mtime_msec) {
|
||||||
self->thumbnail = cairo_surface_reference(cached);
|
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 {
|
} else {
|
||||||
cairo_surface_t *found = fiv_thumbnail_lookup(
|
cairo_surface_t *found = fiv_thumbnail_lookup(
|
||||||
self->uri, self->mtime_msec, browser->item_size);
|
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_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(
|
|
||||||
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));
|
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
|
static void
|
||||||
|
@ -629,11 +644,23 @@ thumbnailer_next(Thumbnailer *t)
|
||||||
self->thumbnailers_queue =
|
self->thumbnailers_queue =
|
||||||
g_list_delete_link(self->thumbnailers_queue, 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;
|
GError *error = NULL;
|
||||||
t->minion = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error,
|
t->minion = g_subprocess_newv(t->target->icon ? argv_faster : argv_slower,
|
||||||
PROJECT_NAME, "--thumbnail",
|
G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error);
|
||||||
fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name, "--",
|
|
||||||
t->target->uri, NULL);
|
|
||||||
if (error) {
|
if (error) {
|
||||||
g_warning("%s", error->message);
|
g_warning("%s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
|
|
Loading…
Reference in New Issue