diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-09-18 13:25:47 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-09-18 17:35:00 -0400 |
commit | 4a89cfe2c938512c4580d3697cc08a64e41f5f36 (patch) | |
tree | d61d6a74e72d00cb93f061741f6c0750c518341b | |
parent | c9135546b6bd764c6fd07add93f7fc3ca246d2d8 (diff) | |
download | gtk+-4a89cfe2c938512c4580d3697cc08a64e41f5f36.tar.gz |
Add delayed loading for textures
Add a private GdkPaintable implementation that
loads a texture in a thread, and does not show
anything until the texture is loaded. This avoid
blocking on image loading in the main thread.
-rw-r--r-- | gtk/gdkpixbufutils.c | 14 | ||||
-rw-r--r-- | gtk/gtkloader.c | 186 | ||||
-rw-r--r-- | gtk/gtkloaderprivate.h | 35 | ||||
-rw-r--r-- | gtk/meson.build | 1 |
4 files changed, 229 insertions, 7 deletions
diff --git a/gtk/gdkpixbufutils.c b/gtk/gdkpixbufutils.c index df8c542deb..59c87a97e9 100644 --- a/gtk/gdkpixbufutils.c +++ b/gtk/gdkpixbufutils.c @@ -580,7 +580,7 @@ gdk_paintable_new_from_bytes_scaled (GBytes *bytes, int scale_factor) { LoaderData loader_data; - GdkTexture *texture; + GdkPaintable *inner; GdkPaintable *paintable; loader_data.scale_factor = scale_factor; @@ -588,8 +588,8 @@ gdk_paintable_new_from_bytes_scaled (GBytes *bytes, if (gdk_texture_can_load (bytes)) { /* We know these formats can't be scaled */ - texture = gdk_texture_new_from_bytes (bytes, NULL); - if (texture == NULL) + inner = GDK_PAINTABLE (gdk_texture_new_from_bytes (bytes, NULL)); + if (inner == NULL) return NULL; } else @@ -608,16 +608,16 @@ gdk_paintable_new_from_bytes_scaled (GBytes *bytes, if (!success) return NULL; - texture = gdk_texture_new_for_pixbuf (gdk_pixbuf_loader_get_pixbuf (loader)); + inner = GDK_PAINTABLE (gdk_texture_new_for_pixbuf (gdk_pixbuf_loader_get_pixbuf (loader))); g_object_unref (loader); } if (loader_data.scale_factor != 1) - paintable = gtk_scaler_new (GDK_PAINTABLE (texture), loader_data.scale_factor); + paintable = gtk_scaler_new (inner, loader_data.scale_factor); else - paintable = g_object_ref ((GdkPaintable *)texture); + paintable = g_object_ref ((GdkPaintable *)inner); - g_object_unref (texture); + g_object_unref (inner); return paintable; } diff --git a/gtk/gtkloader.c b/gtk/gtkloader.c new file mode 100644 index 0000000000..737b0685bc --- /dev/null +++ b/gtk/gtkloader.c @@ -0,0 +1,186 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + +#include "config.h" + +#include "gtkloaderprivate.h" + +#include "gtksnapshot.h" + +struct _GtkLoader +{ + GObject parent_instance; + + GdkTexture *texture; +}; + +struct _GtkLoaderClass +{ + GObjectClass parent_class; +}; + +static void +gtk_loader_paintable_snapshot (GdkPaintable *paintable, + GdkSnapshot *snapshot, + double width, + double height) +{ + GtkLoader *self = GTK_LOADER (paintable); + + if (self->texture) + gdk_paintable_snapshot (GDK_PAINTABLE (self->texture), snapshot, width, height); +} + +static GdkPaintable * +gtk_loader_paintable_get_current_image (GdkPaintable *paintable) +{ + GtkLoader *self = GTK_LOADER (paintable); + + if (self->texture) + return gdk_paintable_get_current_image (GDK_PAINTABLE (self->texture)); + + // FIXME: return a loading image + return NULL; +} + +static int +gtk_loader_paintable_get_intrinsic_width (GdkPaintable *paintable) +{ + GtkLoader *self = GTK_LOADER (paintable); + + if (self->texture) + return gdk_paintable_get_intrinsic_width (GDK_PAINTABLE (self->texture)); + + return 16; +} + +static int +gtk_loader_paintable_get_intrinsic_height (GdkPaintable *paintable) +{ + GtkLoader *self = GTK_LOADER (paintable); + + if (self->texture) + return gdk_paintable_get_intrinsic_height (GDK_PAINTABLE (self->texture)); + + return 16; +} + +static double +gtk_loader_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable) +{ + GtkLoader *self = GTK_LOADER (paintable); + + if (self->texture) + return gdk_paintable_get_intrinsic_aspect_ratio (GDK_PAINTABLE (self->texture)); + + return 0; +}; + +static void +gtk_loader_paintable_init (GdkPaintableInterface *iface) +{ + iface->snapshot = gtk_loader_paintable_snapshot; + iface->get_current_image = gtk_loader_paintable_get_current_image; + iface->get_intrinsic_width = gtk_loader_paintable_get_intrinsic_width; + iface->get_intrinsic_height = gtk_loader_paintable_get_intrinsic_height; + iface->get_intrinsic_aspect_ratio = gtk_loader_paintable_get_intrinsic_aspect_ratio; +} + +G_DEFINE_TYPE_EXTENDED (GtkLoader, gtk_loader, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, + gtk_loader_paintable_init)) + +static void +gtk_loader_dispose (GObject *object) +{ + GtkLoader *self = GTK_LOADER (object); + + g_clear_object (&self->texture); + + G_OBJECT_CLASS (gtk_loader_parent_class)->dispose (object); +} + +static void +gtk_loader_class_init (GtkLoaderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = gtk_loader_dispose; +} + +static void +gtk_loader_init (GtkLoader *self) +{ +} + +static void +load_texture_in_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GBytes *bytes = task_data; + GdkTexture *texture; + GError *error = NULL; + + texture = gdk_texture_new_from_bytes (bytes, &error); + + if (texture) + g_task_return_pointer (task, texture, g_object_unref); + else + g_task_return_error (task, error); +} + +static void +texture_finished (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GtkLoader *self = GTK_LOADER (source); + GdkTexture *texture; + GError *error = NULL; + + texture = g_task_propagate_pointer (G_TASK (result), &error); + + if (texture) + { + self->texture = g_object_ref (texture); + + gdk_paintable_invalidate_size (GDK_PAINTABLE (self)); + gdk_paintable_invalidate_contents (GDK_PAINTABLE (self)); + } +} + +GdkPaintable * +gtk_loader_new (GBytes *bytes) +{ + GtkLoader *self; + GTask *task; + + g_return_val_if_fail (bytes != NULL, NULL); + + self = g_object_new (GTK_TYPE_LOADER, NULL); + + task = g_task_new (self, NULL, texture_finished, NULL); + g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref); + g_task_run_in_thread (task, load_texture_in_thread); + g_object_unref (task); + + return GDK_PAINTABLE (self); +} diff --git a/gtk/gtkloaderprivate.h b/gtk/gtkloaderprivate.h new file mode 100644 index 0000000000..4ae56eef5e --- /dev/null +++ b/gtk/gtkloaderprivate.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Matthias Clasen <mclasen@redhat.com> + */ + +#ifndef __GTK_LOADER_H__ +#define __GTK_LOADER_H__ + +#include <gdk/gdk.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_LOADER (gtk_loader_get_type ()) + +G_DECLARE_FINAL_TYPE (GtkLoader, gtk_loader, GTK, LOADER, GObject) + +GdkPaintable * gtk_loader_new (GBytes *bytes); + +G_END_DECLS + +#endif /* __GTK_SCALER_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index bc097fdd6f..747096a7d4 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -116,6 +116,7 @@ gtk_private_sources = files([ 'gtkiconhelper.c', 'gtkjoinedmenu.c', 'gtkkineticscrolling.c', + 'gtkloader.c', 'gtkmagnifier.c', 'gtkmenusectionbox.c', 'gtkmenutracker.c', |