From 1759d27da9a17d0130df7078bfa9355618047b6c Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 11 Sep 2021 21:18:56 +0200 Subject: [PATCH] memorytexture: Add support for HDR formats Also sanitize the input bytes so the strides match alignment requirements of the data types. --- gdk/gdkmemorytexture.c | 297 ++++++++++++++++++++++++++++++++++++++++- gdk/gdkmemorytexture.h | 20 +++ 2 files changed, 312 insertions(+), 5 deletions(-) diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c index 6d0c861eec..631357bcc8 100644 --- a/gdk/gdkmemorytexture.c +++ b/gdk/gdkmemorytexture.c @@ -20,6 +20,7 @@ #include "config.h" #include "gdkmemorytextureprivate.h" +#include "gsk/ngl/fp16private.h" /** * GdkMemoryTexture: @@ -49,6 +50,10 @@ gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format) { switch (format) { + case GDK_MEMORY_R8G8B8: + case GDK_MEMORY_B8G8R8: + return 3; + case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED: case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED: case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED: @@ -58,9 +63,19 @@ gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format) case GDK_MEMORY_A8B8G8R8: return 4; - case GDK_MEMORY_R8G8B8: - case GDK_MEMORY_B8G8R8: - return 3; + case GDK_MEMORY_R16G16B16: + case GDK_MEMORY_R16G16B16_FLOAT: + return 6; + + case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: + case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: + return 8; + + case GDK_MEMORY_R32G32B32_FLOAT: + return 12; + + case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: + return 16; case GDK_MEMORY_N_FORMATS: default: @@ -69,6 +84,41 @@ gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format) } } +static gsize +gdk_memory_format_alignment (GdkMemoryFormat format) +{ + switch (format) + { + case GDK_MEMORY_R8G8B8: + case GDK_MEMORY_B8G8R8: + case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED: + case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED: + case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED: + case GDK_MEMORY_B8G8R8A8: + case GDK_MEMORY_A8R8G8B8: + case GDK_MEMORY_R8G8B8A8: + case GDK_MEMORY_A8B8G8R8: + return G_ALIGNOF (guchar); + + case GDK_MEMORY_R16G16B16: + case GDK_MEMORY_R16G16B16_FLOAT: + return G_ALIGNOF (guint16); + + case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: + case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: + return G_ALIGNOF (guint16); + + case GDK_MEMORY_R32G32B32_FLOAT: + case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: + return G_ALIGNOF (float); + + case GDK_MEMORY_N_FORMATS: + default: + g_assert_not_reached (); + return G_ALIGNOF (double); + } +} + static void gdk_memory_texture_dispose (GObject *object) { @@ -133,6 +183,41 @@ gdk_memory_texture_init (GdkMemoryTexture *self) { } +static GBytes * +gdk_memory_sanitize (GBytes *bytes, + int width, + int height, + GdkMemoryFormat format, + gsize stride, + gsize *out_stride) +{ + gsize align, size, copy_stride, bpp; + const guchar *data; + guchar *copy; + int y; + + data = g_bytes_get_data (bytes, &size); + align = gdk_memory_format_alignment (format); + + if (GPOINTER_TO_SIZE (data) % align == 0 && + stride % align == 0) + { + *out_stride = stride; + return g_bytes_ref (bytes); + } + + bpp = gdk_memory_format_bytes_per_pixel (format); + copy_stride = bpp * width; + /* align to multiples of 4, just to be sure */ + copy_stride = (copy_stride + 3) & ~3; + copy = g_malloc (copy_stride * height); + for (y = 0; y < height; y++) + memcpy (copy + y * copy_stride, data + y * stride, bpp * width); + + *out_stride = copy_stride; + return g_bytes_new_take (copy, copy_stride * height); +} + /** * gdk_memory_texture_new: * @width: the width of the texture @@ -157,13 +242,20 @@ gdk_memory_texture_new (int width, { GdkMemoryTexture *self; + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (stride >= width * gdk_memory_format_bytes_per_pixel (format), NULL); + + bytes = gdk_memory_sanitize (bytes, width, height, format, stride, &stride); + self = g_object_new (GDK_TYPE_MEMORY_TEXTURE, "width", width, "height", height, NULL); self->format = format; - self->bytes = g_bytes_ref (bytes); + self->bytes = bytes; self->stride = stride; return GDK_TEXTURE (self); @@ -304,6 +396,109 @@ SWIZZLE_PREMULTIPLY (3,0,1,2, 0,1,2,3) SWIZZLE_PREMULTIPLY (3,0,1,2, 3,0,1,2) SWIZZLE_PREMULTIPLY (3,0,1,2, 0,3,2,1) +#define CONVERT_FUNC(name,suffix,R,G,B,A,step) \ +static void \ +convert_ ## name ## _to_ ## suffix (guchar *dest_data, \ + gsize dest_stride, \ + const guchar *src_data, \ + gsize src_stride, \ + gsize width, \ + gsize height) \ +{ \ + gsize x, y; \ +\ + for (y = 0; y < height; y++) \ + { \ + for (x = 0; x < width; x++) \ + { \ + guchar conv[4]; \ + convert_pixel_ ## name (conv, src_data + step * x); \ + dest_data[4 * x + R] = conv[0]; \ + dest_data[4 * x + G] = conv[1]; \ + dest_data[4 * x + B] = conv[2]; \ + dest_data[4 * x + A] = conv[3]; \ + } \ +\ + dest_data += dest_stride; \ + src_data += src_stride; \ + } \ +} + +#define CONVERT_FUNCS(name,step) \ +CONVERT_FUNC(name, download_le, 2, 1, 0, 3, step) \ +CONVERT_FUNC(name, download_be, 1, 2, 3, 0, step) \ +CONVERT_FUNC(name, gles_rgba, 0, 1, 2, 3, step) \ + +static inline void +convert_pixel_rgb16 (guchar *dest_data, const guchar *src_data) +{ + const guint16 *src = (const guint16 *) src_data; + dest_data[0] = (guchar)(src[0] >> 8); + dest_data[1] = (guchar)(src[1] >> 8); + dest_data[2] = (guchar)(src[2] >> 8); + dest_data[3] = 0xFF; +} +CONVERT_FUNCS(rgb16, 3 * sizeof (guint16)) + +static inline void +convert_pixel_rgba16 (guchar *dest_data, const guchar *src_data) +{ + const guint16 *src = (const guint16 *) src_data; + dest_data[0] = (guchar)(src[0] >> 8); + dest_data[1] = (guchar)(src[1] >> 8); + dest_data[2] = (guchar)(src[2] >> 8); + dest_data[3] = (guchar)(src[3] >> 8); +} +CONVERT_FUNCS(rgba16, 4 * sizeof (guint16)) + +static inline void +convert_pixel_rgb16f (guchar *dest_data, const guchar *src_data) +{ + float src[4]; + guint16 tmp[4]; + memcpy(tmp, src_data, sizeof(guint16) * 3); + half_to_float4(tmp, src); + dest_data[0] = CLAMP (src[0] * 256.f, 0.f, 255.f); + dest_data[1] = CLAMP (src[1] * 256.f, 0.f, 255.f); + dest_data[2] = CLAMP (src[2] * 256.f, 0.f, 255.f); + dest_data[3] = 0xFF; +} +CONVERT_FUNCS(rgb16f, 3 * sizeof (guint16)) + +static inline void +convert_pixel_rgba16f (guchar *dest_data, const guchar *src_data) +{ + float src[4]; + half_to_float4((const guint16 *) src_data, src); + dest_data[0] = CLAMP (src[0] * 256.f, 0.f, 255.f); + dest_data[1] = CLAMP (src[1] * 256.f, 0.f, 255.f); + dest_data[2] = CLAMP (src[2] * 256.f, 0.f, 255.f); + dest_data[3] = CLAMP (src[3] * 256.f, 0.f, 255.f); +} +CONVERT_FUNCS(rgba16f, 4 * sizeof (guint16)) + +static inline void +convert_pixel_rgb32f (guchar *dest_data, const guchar *src_data) +{ + float *src = (float *) src_data; + dest_data[0] = CLAMP (src[0] * 256.f, 0.f, 255.f); + dest_data[1] = CLAMP (src[1] * 256.f, 0.f, 255.f); + dest_data[2] = CLAMP (src[2] * 256.f, 0.f, 255.f); + dest_data[3] = 0xFF; +} +CONVERT_FUNCS(rgb32f, 3 * sizeof (float)) + +static inline void +convert_pixel_rgba32f (guchar *dest_data, const guchar *src_data) +{ + float *src = (float *) src_data; + dest_data[0] = CLAMP (src[0] * 256.f, 0.f, 255.f); + dest_data[1] = CLAMP (src[1] * 256.f, 0.f, 255.f); + dest_data[2] = CLAMP (src[2] * 256.f, 0.f, 255.f); + dest_data[3] = CLAMP (src[3] * 256.f, 0.f, 255.f); +} +CONVERT_FUNCS(rgba32f, 4 * sizeof (float)) + typedef void (* ConversionFunc) (guchar *dest_data, gsize dest_stride, const guchar *src_data, @@ -321,7 +516,13 @@ static ConversionFunc converters[GDK_MEMORY_N_FORMATS][GDK_MEMORY_N_CONVERSIONS] { convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012, convert_swizzle_premultiply_3012_3012 }, { convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321, convert_swizzle_premultiply_3012_0321 }, { convert_swizzle_opaque_3210, convert_swizzle_opaque_0123, convert_swizzle_opaque_3012 }, - { convert_swizzle_opaque_3012, convert_swizzle_opaque_0321, convert_swizzle_opaque_3210 } + { convert_swizzle_opaque_3012, convert_swizzle_opaque_0321, convert_swizzle_opaque_3210 }, + { convert_rgb16_to_download_le, convert_rgb16_to_download_be, convert_rgb16_to_gles_rgba }, + { convert_rgba16_to_download_le, convert_rgba16_to_download_be, convert_rgba16_to_gles_rgba }, + { convert_rgb16f_to_download_le, convert_rgb16f_to_download_be, convert_rgb16f_to_gles_rgba }, + { convert_rgba16f_to_download_le, convert_rgba16f_to_download_be, convert_rgba16f_to_gles_rgba }, + { convert_rgb32f_to_download_le, convert_rgb32f_to_download_be, convert_rgb32f_to_gles_rgba }, + { convert_rgba32f_to_download_le, convert_rgba32f_to_download_be, convert_rgba32f_to_gles_rgba } }; void @@ -372,6 +573,74 @@ gdk_memory_convert (guchar *dest_data, } \ }G_STMT_END +#define CONVERT_FLOAT_PIXEL(func,step) G_STMT_START{\ + for (y = 0; y < height; y++) \ + { \ + for (x = 0; x < width; x++) \ + { \ + func (dest_data + 4 * x, src_data + step * x); \ + } \ +\ + dest_data += dest_stride; \ + src_data += src_stride; \ + } \ +}G_STMT_END + +static inline void +convert_rgb16_to_float (float *dest, const guchar *src_data) +{ + const guint16 *src = (const guint16 *) src_data; + dest[0] = src[0] / 65535.f; + dest[1] = src[1] / 65535.f; + dest[2] = src[2] / 65535.f; + dest[3] = 1.0; +} + +static inline void +convert_rgba16_to_float (float *dest, const guchar *src_data) +{ + const guint16 *src = (const guint16 *) src_data; + dest[0] = src[0] / 65535.f; + dest[1] = src[1] / 65535.f; + dest[2] = src[2] / 65535.f; + dest[3] = 1.0; +} + +static inline void +convert_rgb16f_to_float (float *dest, const guchar *src_data) +{ + guint16 tmp[4]; + memcpy(tmp, src_data, sizeof(guint16) * 3); + tmp[3] = FP16_ONE; + half_to_float4 (tmp, dest); +} + +static inline void +convert_rgba16f_to_float (float *dest, const guchar *src_data) +{ + half_to_float4 ((const guint16 *) src_data, dest); +} + +static inline void +convert_rgb32f_to_float (float *dest, const guchar *src_data) +{ + const float *src = (const float *) src_data; + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 1.0; +} + +static inline void +convert_rgba32f_to_float (float *dest, const guchar *src_data) +{ + const float *src = (const float *) src_data; + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; +} + void gdk_memory_convert_to_float (float *dest_data, gsize dest_stride, @@ -412,6 +681,24 @@ gdk_memory_convert_to_float (float *dest_data, case GDK_MEMORY_B8G8R8: CONVERT_FLOAT (2, 1, 0, -1, FALSE); break; + case GDK_MEMORY_R16G16B16: + CONVERT_FLOAT_PIXEL (convert_rgb16_to_float, 3 * sizeof (guint16)); + break; + case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: + CONVERT_FLOAT_PIXEL (convert_rgba16_to_float, 4 * sizeof (guint16)); + break; + case GDK_MEMORY_R16G16B16_FLOAT: + CONVERT_FLOAT_PIXEL (convert_rgb16f_to_float, 3 * sizeof (guint16)); + break; + case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: + CONVERT_FLOAT_PIXEL (convert_rgba16f_to_float, 4 * sizeof (guint16)); + break; + case GDK_MEMORY_R32G32B32_FLOAT: + CONVERT_FLOAT_PIXEL (convert_rgb32f_to_float, 3 * sizeof (float)); + break; + case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: + CONVERT_FLOAT_PIXEL (convert_rgba32f_to_float, 4 * sizeof (float)); + break; case GDK_MEMORY_N_FORMATS: default: diff --git a/gdk/gdkmemorytexture.h b/gdk/gdkmemorytexture.h index b9f1f21282..114922153e 100644 --- a/gdk/gdkmemorytexture.h +++ b/gdk/gdkmemorytexture.h @@ -42,6 +42,20 @@ G_BEGIN_DECLS * @GDK_MEMORY_A8B8G8R8: 4 bytes; for alpha, blue, green, red. * @GDK_MEMORY_R8G8B8: 3 bytes; for red, green, blue. The data is opaque. * @GDK_MEMORY_B8G8R8: 3 bytes; for blue, green, red. The data is opaque. + * @GDK_MEMORY_R16G16B16: 3 guint16 values; for red, green, blue. Since 4.6 + * @GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: 4 guint16 values; for red, green, + * blue, alpha. The color values are premultiplied with the alpha value. + * Since 4.6 + * @GDK_MEMORY_R16G16B16_FLOAT: 3 half-float values; for red, green, blue. + * The data is opaque. Since 4.6 + * @GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: 4 half-float values; for + * red, green, blue and alpha. The color values are premultiplied with + * the alpha value. Since 4.6 + * @GDK_MEMORY_B32G32R32_FLOAT: 3 float values; for blue, green, red. + * The data is opaque. Since 4.6 + * @GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: 4 float values; for + * red, green, blue and alpha. The color values are premultiplied with + * the alpha value. Since 4.6 * @GDK_MEMORY_N_FORMATS: The number of formats. This value will change as * more formats get added, so do not rely on its concrete integer. * @@ -67,6 +81,12 @@ typedef enum { GDK_MEMORY_A8B8G8R8, GDK_MEMORY_R8G8B8, GDK_MEMORY_B8G8R8, + GDK_MEMORY_R16G16B16, + GDK_MEMORY_R16G16B16A16_PREMULTIPLIED, + GDK_MEMORY_R16G16B16_FLOAT, + GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED, + GDK_MEMORY_R32G32B32_FLOAT, + GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED, GDK_MEMORY_N_FORMATS } GdkMemoryFormat;