Compare commits

..

No commits in common. "35c1f2c8ba43f6e8c567046247395042f775f80e" and "231b77e6c0084941430ce2cd0069166d13bf9172" have entirely different histories.

2 changed files with 55 additions and 93 deletions

View File

@ -41,17 +41,6 @@
// The glow is actually a glowing margin, the border is rendered in two parts. // The glow is actually a glowing margin, the border is rendered in two parts.
// //
typedef struct entry Entry;
typedef struct item Item;
typedef struct row Row;
typedef struct _Thumbnailer {
FivBrowser *self; ///< Parent browser
Entry *target; ///< Currently processed Entry pointer
GSubprocess *minion; ///< A slave for the current queue head
GCancellable *cancel; ///< Cancellable handle
} Thumbnailer;
struct _FivBrowser { struct _FivBrowser {
GtkWidget parent_instance; GtkWidget parent_instance;
@ -64,9 +53,9 @@ struct _FivBrowser {
GArray *layouted_rows; ///< []Row GArray *layouted_rows; ///< []Row
int selected; int selected;
Thumbnailer *thumbnailers; ///< Parallelized thumbnailers GList *thumbnail_queue; ///< Entry pointers
size_t thumbnailers_len; ///< Thumbnailers array size GSubprocess *thumbnailer; ///< A slave for the current queue head
GList *thumbnailers_queue; ///< Queued up Entry pointers GCancellable *thumbnail_cancel; ///< Cancellable handle
GdkCursor *pointer; ///< Cached pointer cursor GdkCursor *pointer; ///< Cached pointer cursor
cairo_surface_t *glow; ///< CAIRO_FORMAT_A8 mask cairo_surface_t *glow; ///< CAIRO_FORMAT_A8 mask
@ -74,6 +63,10 @@ struct _FivBrowser {
int item_border_y; ///< T/B .item margin + border int item_border_y; ///< T/B .item margin + border
}; };
typedef struct entry Entry;
typedef struct item Item;
typedef struct row Row;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct entry { struct entry {
@ -457,9 +450,9 @@ reload_thumbnails(FivBrowser *self)
gtk_widget_queue_resize(GTK_WIDGET(self)); gtk_widget_queue_resize(GTK_WIDGET(self));
} }
// --- Minion management ------------------------------------------------------- // --- Slave management --------------------------------------------------------
static gboolean thumbnailer_next(Thumbnailer *thumbnailer); static void thumbnailer_next(FivBrowser *self);
static void static void
thumbnailer_reprocess_entry(FivBrowser *self, Entry *entry) thumbnailer_reprocess_entry(FivBrowser *self, Entry *entry)
@ -473,8 +466,7 @@ static void
on_thumbnailer_ready(GObject *object, GAsyncResult *res, gpointer user_data) on_thumbnailer_ready(GObject *object, GAsyncResult *res, gpointer user_data)
{ {
GSubprocess *subprocess = G_SUBPROCESS(object); GSubprocess *subprocess = G_SUBPROCESS(object);
Thumbnailer *thumbnailer = user_data; FivBrowser *self = FIV_BROWSER(user_data);
GError *error = NULL; GError *error = NULL;
if (!g_subprocess_wait_check_finish(subprocess, res, &error)) { if (!g_subprocess_wait_check_finish(subprocess, res, &error)) {
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
@ -487,78 +479,67 @@ on_thumbnailer_ready(GObject *object, GAsyncResult *res, gpointer user_data)
g_error_free(error); g_error_free(error);
} }
g_return_if_fail(subprocess == thumbnailer->minion);
gboolean succeeded = g_subprocess_get_if_exited(subprocess) && gboolean succeeded = g_subprocess_get_if_exited(subprocess) &&
g_subprocess_get_exit_status(subprocess) == EXIT_SUCCESS; g_subprocess_get_exit_status(subprocess) == EXIT_SUCCESS;
g_clear_object(&thumbnailer->minion); g_clear_object(&self->thumbnailer);
if (!thumbnailer->target) { if (!self->thumbnail_queue) {
g_warning("finished thumbnailing an unknown image"); g_warning("finished thumbnailing an unknown image");
return; return;
} }
Entry *entry = self->thumbnail_queue->data;
self->thumbnail_queue =
g_list_delete_link(self->thumbnail_queue, self->thumbnail_queue);
if (succeeded) if (succeeded)
thumbnailer_reprocess_entry(thumbnailer->self, thumbnailer->target); thumbnailer_reprocess_entry(self, entry);
thumbnailer->target = NULL; thumbnailer_next(self);
thumbnailer_next(thumbnailer);
} }
static gboolean static void
thumbnailer_next(Thumbnailer *thumbnailer) thumbnailer_next(FivBrowser *self)
{ {
// TODO(p): Ideally, try to keep the minions alive. // TODO(p): At least launch multiple thumbnailers in parallel.
FivBrowser *self = thumbnailer->self; // Ideally, try to keep them alive.
GList *link = self->thumbnailers_queue; GList *link = self->thumbnail_queue;
if (!link) if (!link)
return FALSE; return;
thumbnailer->target = link->data;
self->thumbnailers_queue =
g_list_delete_link(self->thumbnailers_queue, self->thumbnailers_queue);
const Entry *entry = link->data;
GError *error = NULL; GError *error = NULL;
thumbnailer->minion = g_subprocess_new(G_SUBPROCESS_FLAGS_NONE, &error, self->thumbnailer = g_subprocess_new(G_SUBPROCESS_FLAGS_NONE, &error,
PROJECT_NAME, "--thumbnail", PROJECT_NAME, "--thumbnail",
fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name, "--", fiv_thumbnail_sizes[self->item_size].thumbnail_spec_name, "--",
thumbnailer->target->uri, NULL); entry->uri, NULL);
if (error) { if (error) {
g_warning("%s", error->message); g_warning("%s", error->message);
g_error_free(error); g_error_free(error);
return FALSE; return;
} }
thumbnailer->cancel = g_cancellable_new(); self->thumbnail_cancel = g_cancellable_new();
g_subprocess_wait_check_async(thumbnailer->minion, thumbnailer->cancel, g_subprocess_wait_check_async(
on_thumbnailer_ready, thumbnailer); self->thumbnailer, self->thumbnail_cancel, on_thumbnailer_ready, self);
return TRUE;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
thumbnailers_abort(FivBrowser *self)
{
g_list_free(self->thumbnailers_queue);
self->thumbnailers_queue = NULL;
for (size_t i = 0; i < self->thumbnailers_len; i++) {
Thumbnailer *thumbnailer = self->thumbnailers + i;
if (thumbnailer->cancel) {
g_cancellable_cancel(thumbnailer->cancel);
g_clear_object(&thumbnailer->cancel);
}
// Just let them exit on their own.
g_clear_object(&thumbnailer->minion);
thumbnailer->target = NULL;
}
} }
static void static void
thumbnailers_start(FivBrowser *self) thumbnailer_abort(FivBrowser *self)
{ {
thumbnailers_abort(self); if (self->thumbnail_cancel) {
g_cancellable_cancel(self->thumbnail_cancel);
g_clear_object(&self->thumbnail_cancel);
}
// Just let it exit on its own.
g_clear_object(&self->thumbnailer);
g_list_free(self->thumbnail_queue);
self->thumbnail_queue = NULL;
}
static void
thumbnailer_start(FivBrowser *self)
{
thumbnailer_abort(self);
// TODO(p): Leave out all paths containing .cache/thumbnails altogether. // TODO(p): Leave out all paths containing .cache/thumbnails altogether.
gchar *thumbnails_dir = fiv_thumbnail_get_root(); gchar *thumbnails_dir = fiv_thumbnail_get_root();
@ -581,11 +562,8 @@ thumbnailers_start(FivBrowser *self)
lq = g_list_prepend(lq, entry); lq = g_list_prepend(lq, entry);
} }
self->thumbnailers_queue = g_list_concat(missing, lq); self->thumbnail_queue = g_list_concat(missing, lq);
for (size_t i = 0; i < self->thumbnailers_len; i++) { thumbnailer_next(self);
if (!thumbnailer_next(self->thumbnailers + i))
break;
}
} }
// --- Context menu------------------------------------------------------------- // --- Context menu-------------------------------------------------------------
@ -772,7 +750,7 @@ static void
fiv_browser_finalize(GObject *gobject) fiv_browser_finalize(GObject *gobject)
{ {
FivBrowser *self = FIV_BROWSER(gobject); FivBrowser *self = FIV_BROWSER(gobject);
thumbnailers_abort(self); thumbnailer_abort(self);
g_array_free(self->entries, TRUE); g_array_free(self->entries, TRUE);
g_array_free(self->layouted_rows, TRUE); g_array_free(self->layouted_rows, TRUE);
if (self->model) { if (self->model) {
@ -1168,15 +1146,9 @@ fiv_browser_init(FivBrowser *self)
g_array_set_clear_func(self->entries, (GDestroyNotify) entry_free); g_array_set_clear_func(self->entries, (GDestroyNotify) entry_free);
self->layouted_rows = g_array_new(FALSE, TRUE, sizeof(Row)); self->layouted_rows = g_array_new(FALSE, TRUE, sizeof(Row));
g_array_set_clear_func(self->layouted_rows, (GDestroyNotify) row_free); g_array_set_clear_func(self->layouted_rows, (GDestroyNotify) row_free);
self->selected = -1;
self->thumbnailers_len = g_get_num_processors();
self->thumbnailers =
g_malloc0_n(self->thumbnailers_len, sizeof *self->thumbnailers);
for (size_t i = 0; i < self->thumbnailers_len; i++)
self->thumbnailers[i].self = self;
set_item_size(self, FIV_THUMBNAIL_SIZE_NORMAL); set_item_size(self, FIV_THUMBNAIL_SIZE_NORMAL);
self->selected = -1;
self->glow = cairo_image_surface_create(CAIRO_FORMAT_A1, 0, 0); self->glow = cairo_image_surface_create(CAIRO_FORMAT_A1, 0, 0);
g_signal_connect_swapped(gtk_settings_get_default(), g_signal_connect_swapped(gtk_settings_get_default(),
@ -1191,7 +1163,7 @@ on_model_files_changed(FivIoModel *model, FivBrowser *self)
g_return_if_fail(model == self->model); g_return_if_fail(model == self->model);
// TODO(p): Later implement arguments. // TODO(p): Later implement arguments.
thumbnailers_abort(self); thumbnailer_abort(self);
g_array_set_size(self->entries, 0); g_array_set_size(self->entries, 0);
g_array_set_size(self->layouted_rows, 0); g_array_set_size(self->layouted_rows, 0);
@ -1204,7 +1176,7 @@ on_model_files_changed(FivIoModel *model, FivBrowser *self)
g_ptr_array_free(files, TRUE); g_ptr_array_free(files, TRUE);
reload_thumbnails(self); reload_thumbnails(self);
thumbnailers_start(self); thumbnailer_start(self);
} }
GtkWidget * GtkWidget *

View File

@ -564,17 +564,10 @@ load_wuffs_frame(struct load_wuffs_frame_context *ctx, GError **error)
return false; return false;
} }
// Wuffs' test/data/animated-red-blue.gif, e.g., needs this handling.
cairo_format_t decode_format = ctx->cairo_format;
if (wuffs_base__frame_config__index(&fc) > 0 &&
wuffs_base__pixel_config__pixel_format(&ctx->cfg.pixcfg).repr ==
WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL)
decode_format = CAIRO_FORMAT_ARGB32;
bool success = false; bool success = false;
unsigned char *targetbuf = NULL; unsigned char *targetbuf = NULL;
cairo_surface_t *surface = cairo_surface_t *surface =
cairo_image_surface_create(decode_format, ctx->width, ctx->height); cairo_image_surface_create(ctx->cairo_format, ctx->width, ctx->height);
cairo_status_t surface_status = cairo_surface_status(surface); cairo_status_t surface_status = cairo_surface_status(surface);
if (surface_status != CAIRO_STATUS_SUCCESS) { if (surface_status != CAIRO_STATUS_SUCCESS) {
set_error(error, cairo_status_to_string(surface_status)); set_error(error, cairo_status_to_string(surface_status));
@ -666,7 +659,6 @@ load_wuffs_frame(struct load_wuffs_frame_context *ctx, GError **error)
cairo_surface_mark_dirty(canvas); cairo_surface_mark_dirty(canvas);
// Apply that frame's disposal method. // Apply that frame's disposal method.
// XXX: We do not expect opaque pictures to receive holes this way.
wuffs_base__rect_ie_u32 bounds = wuffs_base__rect_ie_u32 bounds =
wuffs_base__frame_config__bounds(&ctx->last_fc); wuffs_base__frame_config__bounds(&ctx->last_fc);
// TODO(p): This field needs to be colour-managed. // TODO(p): This field needs to be colour-managed.
@ -694,8 +686,6 @@ load_wuffs_frame(struct load_wuffs_frame_context *ctx, GError **error)
// TODO(p): Implement, it seems tricky. // TODO(p): Implement, it seems tricky.
// Might need another surface to keep track of the state. // Might need another surface to keep track of the state.
break; break;
case WUFFS_BASE__ANIMATION_DISPOSAL__NONE:
break;
} }
// Paint the current frame over that, within its bounds. // Paint the current frame over that, within its bounds.
@ -854,6 +844,8 @@ open_wuffs(wuffs_base__image_decoder *dec, wuffs_base__io_buffer src,
// Wuffs maps tRNS to BGRA in `decoder.decode_trns?`, we should be fine. // Wuffs maps tRNS to BGRA in `decoder.decode_trns?`, we should be fine.
// wuffs_base__pixel_format__transparency() doesn't reflect the image file. // wuffs_base__pixel_format__transparency() doesn't reflect the image file.
// TODO(p): See if wuffs_base__image_config__first_frame_is_opaque() causes
// issues with animations, and eventually ensure an alpha-capable format.
bool opaque = wuffs_base__image_config__first_frame_is_opaque(&ctx.cfg); bool opaque = wuffs_base__image_config__first_frame_is_opaque(&ctx.cfg);
// Wuffs' API is kind of awful--we want to catch wide RGB and wide grey. // Wuffs' API is kind of awful--we want to catch wide RGB and wide grey.
@ -894,8 +886,6 @@ open_wuffs(wuffs_base__image_decoder *dec, wuffs_base__io_buffer src,
ctx.cairo_format = CAIRO_FORMAT_RGB30; ctx.cairo_format = CAIRO_FORMAT_RGB30;
} else if (opaque) { } else if (opaque) {
// BGRX doesn't have as wide swizzler support, namely in GIF. // BGRX doesn't have as wide swizzler support, namely in GIF.
// Moreover, follower frames may still be partly transparent.
// Therefore, we choose to keep "wuffs_format" intact.
ctx.cairo_format = CAIRO_FORMAT_RGB24; ctx.cairo_format = CAIRO_FORMAT_RGB24;
} else if (!ctx.target) { } else if (!ctx.target) {
wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL; wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL;