diff --git a/gdk/gdkgltexture.c b/gdk/gdkgltexture.c index bd69809061..365a3a6ac2 100644 --- a/gdk/gdkgltexture.c +++ b/gdk/gdkgltexture.c @@ -179,6 +179,60 @@ gdk_gl_texture_download (GdkTexture *texture, glBindTexture (GL_TEXTURE_2D, active_texture); } +static void +gdk_gl_texture_do_download_float (GdkTexture *texture, + float *data) +{ + GdkGLTexture *self = GDK_GL_TEXTURE (texture); + int active_texture; + + gdk_gl_context_make_current (self->context); + + glGetIntegerv (GL_TEXTURE_BINDING_2D, &active_texture); + glBindTexture (GL_TEXTURE_2D, self->id); + + glGetTexImage (GL_TEXTURE_2D, + 0, + GL_RGBA, + GL_FLOAT, + data); + + glBindTexture (GL_TEXTURE_2D, active_texture); +} + +static void +gdk_gl_texture_download_float (GdkTexture *texture, + float *data, + gsize stride) +{ + GdkGLTexture *self = GDK_GL_TEXTURE (texture); + int width, height, y; + float *copy; + + if (self->saved) + { + gdk_texture_download_float (self->saved, data, stride); + return; + } + + width = gdk_texture_get_width (texture); + height = gdk_texture_get_height (texture); + + if (stride == width * 4) + { + gdk_gl_texture_do_download_float (texture, data); + return; + } + + copy = g_new (float, width * height * 4); + + gdk_gl_texture_do_download_float (texture, copy); + for (y = 0; y < height; y++) + memcpy (data + y * stride, copy + y * 4 * width, 4 * width); + + g_free (copy); +} + static void gdk_gl_texture_class_init (GdkGLTextureClass *klass) { @@ -187,6 +241,7 @@ gdk_gl_texture_class_init (GdkGLTextureClass *klass) texture_class->download_texture = gdk_gl_texture_download_texture; texture_class->download = gdk_gl_texture_download; + texture_class->download_float = gdk_gl_texture_download_float; gobject_class->dispose = gdk_gl_texture_dispose; } diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c index edb4d741c6..6d0c861eec 100644 --- a/gdk/gdkmemorytexture.c +++ b/gdk/gdkmemorytexture.c @@ -101,6 +101,21 @@ gdk_memory_texture_download (GdkTexture *texture, gdk_texture_get_height (texture)); } +static void +gdk_memory_texture_download_float (GdkTexture *texture, + float *data, + gsize stride) +{ + GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture); + + gdk_memory_convert_to_float (data, stride, + (guchar *) g_bytes_get_data (self->bytes, NULL), + self->stride, + self->format, + gdk_texture_get_width (texture), + gdk_texture_get_height (texture)); +} + static void gdk_memory_texture_class_init (GdkMemoryTextureClass *klass) { @@ -109,6 +124,7 @@ gdk_memory_texture_class_init (GdkMemoryTextureClass *klass) texture_class->download_texture = gdk_memory_texture_download_texture; texture_class->download = gdk_memory_texture_download; + texture_class->download_float = gdk_memory_texture_download_float; gobject_class->dispose = gdk_memory_texture_dispose; } @@ -323,3 +339,82 @@ gdk_memory_convert (guchar *dest_data, converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height); } + +#define CONVERT_FLOAT(R,G,B,A,premultiply) G_STMT_START {\ + for (y = 0; y < height; y++) \ + { \ + for (x = 0; x < width; x++) \ + { \ + if (A >= 0) \ + { \ + dest_data[4 * x + 0] = src_data[4 * x + R] / 255.0f; \ + dest_data[4 * x + 1] = src_data[4 * x + G] / 255.0f; \ + dest_data[4 * x + 2] = src_data[4 * x + B] / 255.0f; \ + dest_data[4 * x + 3] = src_data[4 * x + A] / 255.0f; \ + if (premultiply) \ + { \ + dest_data[4 * x + 0] *= dest_data[4 * x + 3]; \ + dest_data[4 * x + 1] *= dest_data[4 * x + 3]; \ + dest_data[4 * x + 2] *= dest_data[4 * x + 3]; \ + } \ + } \ + else \ + { \ + dest_data[4 * x + 0] = src_data[3 * x + R] / 255.0f; \ + dest_data[4 * x + 1] = src_data[3 * x + G] / 255.0f; \ + dest_data[4 * x + 2] = src_data[3 * x + B] / 255.0f; \ + dest_data[4 * x + 3] = 1.0; \ + } \ + } \ +\ + dest_data += dest_stride; \ + src_data += src_stride; \ + } \ +}G_STMT_END + +void +gdk_memory_convert_to_float (float *dest_data, + gsize dest_stride, + const guchar *src_data, + gsize src_stride, + GdkMemoryFormat src_format, + gsize width, + gsize height) +{ + gsize x, y; + + switch (src_format) + { + case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED: + CONVERT_FLOAT (2, 1, 0, 3, FALSE); + break; + case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED: + CONVERT_FLOAT (1, 2, 3, 0, FALSE); + break; + case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED: + CONVERT_FLOAT (0, 1, 2, 3, FALSE); + break; + case GDK_MEMORY_B8G8R8A8: + CONVERT_FLOAT (2, 1, 0, 3, TRUE); + break; + case GDK_MEMORY_A8R8G8B8: + CONVERT_FLOAT (1, 2, 3, 0, TRUE); + break; + case GDK_MEMORY_R8G8B8A8: + CONVERT_FLOAT (0, 1, 2, 3, TRUE); + break; + case GDK_MEMORY_A8B8G8R8: + CONVERT_FLOAT (3, 2, 1, 0, TRUE); + break; + case GDK_MEMORY_R8G8B8: + CONVERT_FLOAT (0, 1, 2, -1, FALSE); + break; + case GDK_MEMORY_B8G8R8: + CONVERT_FLOAT (2, 1, 0, -1, FALSE); + break; + + case GDK_MEMORY_N_FORMATS: + default: + g_assert_not_reached(); + } +} diff --git a/gdk/gdkmemorytextureprivate.h b/gdk/gdkmemorytextureprivate.h index a450a9a139..ddd9fd1c37 100644 --- a/gdk/gdkmemorytextureprivate.h +++ b/gdk/gdkmemorytextureprivate.h @@ -60,6 +60,14 @@ void gdk_memory_convert (guchar gsize width, gsize height); +void gdk_memory_convert_to_float (float *dest_data, + gsize dest_stride, + const guchar *src_data, + gsize src_stride, + GdkMemoryFormat src_format, + gsize width, + gsize height); + G_END_DECLS diff --git a/gdk/gdktexture.c b/gdk/gdktexture.c index 4afd6ebfdd..7b055238fd 100644 --- a/gdk/gdktexture.c +++ b/gdk/gdktexture.c @@ -134,6 +134,18 @@ gdk_texture_real_download (GdkTexture *texture, g_object_unref (memory_texture); } +static void +gdk_texture_real_download_float (GdkTexture *self, + float *data, + gsize stride) +{ + GdkTexture *memory_texture; + + memory_texture = gdk_texture_download_texture (self); + gdk_texture_download_float (memory_texture, data, stride); + g_object_unref (memory_texture); +} + static void gdk_texture_set_property (GObject *gobject, guint prop_id, @@ -199,6 +211,7 @@ gdk_texture_class_init (GdkTextureClass *klass) klass->download_texture = gdk_texture_real_download_texture; klass->download = gdk_texture_real_download; + klass->download_float = gdk_texture_real_download_float; gobject_class->set_property = gdk_texture_set_property; gobject_class->get_property = gdk_texture_get_property; @@ -485,6 +498,45 @@ gdk_texture_download (GdkTexture *texture, GDK_TEXTURE_GET_CLASS (texture)->download (texture, data, stride); } +/** + * gdk_texture_download_float: + * @texture: a `GdkTexture` + * @data: (array): pointer to enough memory to be filled with the + * downloaded data of @texture + * @stride: rowstride in elements, will usually be equal to + * gdk_texture_get_width() * 4 + * + * Downloads the @texture into local memory in a high dynamic range format. + * + * This may be an expensive operation, as the actual texture data + * may reside on a GPU or on a remote display server and because the data + * may need to be upsampled if it was not already available in this + * format. + * + * You may want to use [method@Gdk.Texture.download] instead if you don't + * need high dynamic range support. + * + * The data format of the downloaded data is equivalent to + * GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED, so every downloaded + * pixel requires 16 bytes of memory. + * + * Note that the caller is responsible to provide sufficiently + * aligned memory to access the resulting data directly as floats. + * + * Since: 4.6 + */ +void +gdk_texture_download_float (GdkTexture *texture, + float *data, + gsize stride) +{ + g_return_if_fail (GDK_IS_TEXTURE (texture)); + g_return_if_fail (data != NULL); + g_return_if_fail (stride >= gdk_texture_get_width (texture) * 4); + + GDK_TEXTURE_GET_CLASS (texture)->download_float (texture, data, stride); +} + GdkTexture * gdk_texture_download_texture (GdkTexture *texture) { diff --git a/gdk/gdktexture.h b/gdk/gdktexture.h index f3d0bc9765..9e3b7a93fa 100644 --- a/gdk/gdktexture.h +++ b/gdk/gdktexture.h @@ -59,6 +59,10 @@ GDK_AVAILABLE_IN_ALL void gdk_texture_download (GdkTexture *texture, guchar *data, gsize stride); +GDK_AVAILABLE_IN_4_6 +void gdk_texture_download_float (GdkTexture *texture, + float *data, + gsize stride); GDK_AVAILABLE_IN_ALL gboolean gdk_texture_save_to_png (GdkTexture *texture, const char *filename); diff --git a/gdk/gdktextureprivate.h b/gdk/gdktextureprivate.h index 1133bf6acc..29fca0a9ba 100644 --- a/gdk/gdktextureprivate.h +++ b/gdk/gdktextureprivate.h @@ -30,6 +30,9 @@ struct _GdkTextureClass { void (* download) (GdkTexture *texture, guchar *data, gsize stride); + void (* download_float) (GdkTexture *texture, + float *data, + gsize stride); }; gpointer gdk_texture_new (const GdkTextureClass *klass,