From 4f17f3ac240117ae38cd83e3c8140a4b2a8dccff Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sun, 12 Sep 2021 04:42:24 +0200 Subject: texture: Add gdk_texture_download_texture() A private vfunc that downloads a texture as a GdkMemoryTexture in whatever format the texture deems best. There are multiple reasons for this: * GLES cannot download the Cairo format. But it can download some format and then just delegate to the GdkMemoryTexture implementation. * All the other download vfuncs (including the ones still coming) can be implemented via download_texture() and delegation, making the interface easier. * We want to implement image loading and saving support. By using download_texture(), we can save in the actual format of the texture. * A potential GdkCompressedTexture could be implemented by just providing this one vfunc as a compress() step. --- gdk/gdkgltexture.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++- gdk/gdkmemorytexture.c | 7 +++++ gdk/gdktexture.c | 36 ++++++++++++++++++++++--- gdk/gdktextureprivate.h | 5 ++++ 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/gdk/gdkgltexture.c b/gdk/gdkgltexture.c index adf075ea91..9b5aa86866 100644 --- a/gdk/gdkgltexture.c +++ b/gdk/gdkgltexture.c @@ -21,7 +21,7 @@ #include "gdkgltextureprivate.h" #include "gdkcairo.h" -#include "gdkmemorytexture.h" +#include "gdkmemorytextureprivate.h" #include "gdktextureprivate.h" #include @@ -70,6 +70,75 @@ gdk_gl_texture_dispose (GObject *object) G_OBJECT_CLASS (gdk_gl_texture_parent_class)->dispose (object); } +static GdkTexture * +gdk_gl_texture_download_texture (GdkTexture *texture) +{ + GdkGLTexture *self = GDK_GL_TEXTURE (texture); + GdkTexture *result; + int active_texture; + GdkMemoryFormat format; + GLint internal_format, gl_format, gl_type; + guchar *data; + gsize stride; + GBytes *bytes; + + if (self->saved) + return g_object_ref (self->saved); + + gdk_gl_context_make_current (self->context); + + glGetIntegerv (GL_TEXTURE_BINDING_2D, &active_texture); + glBindTexture (GL_TEXTURE_2D, self->id); + + glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format); + + switch (internal_format) + { + case GL_RGB8: + format = GDK_MEMORY_R8G8B8; + gl_format = GL_RGB; + gl_type = GL_UNSIGNED_BYTE; + break; + + case GL_RGBA8: + format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED; + gl_format = GL_RGBA; + gl_type = GL_UNSIGNED_BYTE; + break; + + default: + g_warning ("Texture in unexpected format 0x%X (%d). File a bug about adding it to GTK", internal_format, internal_format); + /* fallback to the dumbest possible format + * so that even age old GLES can do it */ + format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED; + gl_format = GL_RGBA; + gl_type = GL_UNSIGNED_BYTE; + break; + } + + stride = gdk_memory_format_bytes_per_pixel (format) * texture->width; + data = g_malloc (stride * texture->height); + + glGetTexImage (GL_TEXTURE_2D, + 0, + gl_format, + gl_type, + data); + + bytes = g_bytes_new_take (data, stride * texture->height); + result = gdk_memory_texture_new (texture->width, + texture->height, + format, + bytes, + stride); + + g_bytes_unref (bytes); + + glBindTexture (GL_TEXTURE_2D, active_texture); + + return result; +} + static void gdk_gl_texture_download (GdkTexture *texture, guchar *data, @@ -112,6 +181,7 @@ gdk_gl_texture_class_init (GdkGLTextureClass *klass) GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + texture_class->download_texture = gdk_gl_texture_download_texture; texture_class->download = gdk_gl_texture_download; gobject_class->dispose = gdk_gl_texture_dispose; } diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c index ab117a01ff..edb4d741c6 100644 --- a/gdk/gdkmemorytexture.c +++ b/gdk/gdkmemorytexture.c @@ -79,6 +79,12 @@ gdk_memory_texture_dispose (GObject *object) G_OBJECT_CLASS (gdk_memory_texture_parent_class)->dispose (object); } +static GdkTexture * +gdk_memory_texture_download_texture (GdkTexture *texture) +{ + return g_object_ref (texture); +} + static void gdk_memory_texture_download (GdkTexture *texture, guchar *data, @@ -101,6 +107,7 @@ gdk_memory_texture_class_init (GdkMemoryTextureClass *klass) GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + texture_class->download_texture = gdk_memory_texture_download_texture; texture_class->download = gdk_memory_texture_download; gobject_class->dispose = gdk_memory_texture_dispose; } diff --git a/gdk/gdktexture.c b/gdk/gdktexture.c index 9ae9f80944..4afd6ebfdd 100644 --- a/gdk/gdktexture.c +++ b/gdk/gdktexture.c @@ -115,12 +115,23 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkTexture, gdk_texture, G_TYPE_OBJECT, #define GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \ g_critical ("Texture of type '%s' does not implement GdkTexture::" # method, G_OBJECT_TYPE_NAME (obj)) +static GdkTexture * +gdk_texture_real_download_texture (GdkTexture *self) +{ + GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download_texture); + return NULL; +} + static void -gdk_texture_real_download (GdkTexture *self, - guchar *data, - gsize stride) +gdk_texture_real_download (GdkTexture *texture, + guchar *data, + gsize stride) { - GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download); + GdkTexture *memory_texture; + + memory_texture = gdk_texture_download_texture (texture); + gdk_texture_download (memory_texture, data, stride); + g_object_unref (memory_texture); } static void @@ -186,6 +197,7 @@ gdk_texture_class_init (GdkTextureClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + klass->download_texture = gdk_texture_real_download_texture; klass->download = gdk_texture_real_download; gobject_class->set_property = gdk_texture_set_property; @@ -473,6 +485,22 @@ gdk_texture_download (GdkTexture *texture, GDK_TEXTURE_GET_CLASS (texture)->download (texture, data, stride); } +GdkTexture * +gdk_texture_download_texture (GdkTexture *texture) +{ + g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL); + + g_object_ref (texture); + while (!GDK_IS_MEMORY_TEXTURE (texture)) + { + GdkTexture *downloaded = GDK_TEXTURE_GET_CLASS (texture)->download_texture (texture); + g_object_unref (texture); + texture = downloaded; + } + + return texture; +} + gboolean gdk_texture_set_render_data (GdkTexture *self, gpointer key, diff --git a/gdk/gdktextureprivate.h b/gdk/gdktextureprivate.h index e2e7dc2bae..1133bf6acc 100644 --- a/gdk/gdktextureprivate.h +++ b/gdk/gdktextureprivate.h @@ -24,6 +24,9 @@ struct _GdkTexture struct _GdkTextureClass { GObjectClass parent_class; + /* mandatory: Download into a GdkMemoryTexture */ + GdkTexture * (* download_texture) (GdkTexture *texture); + /* optional */ void (* download) (GdkTexture *texture, guchar *data, gsize stride); @@ -34,6 +37,8 @@ gpointer gdk_texture_new (const GdkTextureClass int height); GdkTexture * gdk_texture_new_for_surface (cairo_surface_t *surface); cairo_surface_t * gdk_texture_download_surface (GdkTexture *texture); +/* NB: GdkMemoryTexture */ +GdkTexture * gdk_texture_download_texture (GdkTexture *texture); gboolean gdk_texture_set_render_data (GdkTexture *self, gpointer key, -- cgit v1.2.1