[gtk/gamma-shenanigans: 1/7] Support 16bit textures




commit 1c90cf200e6d517260aae25fc8e67f4255fd9641
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Sep 5 17:37:52 2021 -0400

    Support 16bit textures
    
    Add GDK_MEMORY_R16G16B16A16_PREMULTIPLIED, which
    matches the libpng PNG_FORMAT_LINEAR_RGB_ALPHA
    format. This format will be used to provide
    linear (gamma-corrected) input in future commits.
    
    GL can upload this format directly. This is not only
    less work for us, it also avoids losing precision
    during upload.

 gdk/gdkglcontext.c            | 19 ++++++++++++++++---
 gdk/gdkmemorytexture.c        | 37 ++++++++++++++++++++++++++++++++++++-
 gdk/gdkmemorytexture.h        |  5 +++++
 testsuite/gdk/memorytexture.c | 15 +++++++++------
 4 files changed, 66 insertions(+), 10 deletions(-)
---
diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c
index 58dba198ab..464612e616 100644
--- a/gdk/gdkglcontext.c
+++ b/gdk/gdkglcontext.c
@@ -229,6 +229,7 @@ gdk_gl_context_upload_texture (GdkGLContext    *context,
 {
   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
   guchar *copy = NULL;
+  guint gl_internalformat;
   guint gl_format;
   guint gl_type;
   guint bpp;
@@ -250,6 +251,7 @@ gdk_gl_context_upload_texture (GdkGLContext    *context,
         }
 
       bpp = 4;
+      gl_internalformat = GL_RGBA8;
       gl_format = GL_RGBA;
       gl_type = GL_UNSIGNED_BYTE;
     }
@@ -257,22 +259,32 @@ gdk_gl_context_upload_texture (GdkGLContext    *context,
     {
       if (data_format == GDK_MEMORY_DEFAULT) /* Cairo surface format */
         {
+          gl_internalformat = GL_RGBA8;
           gl_format = GL_BGRA;
           gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
           bpp = 4;
         }
       else if (data_format == GDK_MEMORY_R8G8B8) /* Pixmap non-alpha data */
         {
+          gl_internalformat = GL_RGBA8;
           gl_format = GL_RGB;
           gl_type = GL_UNSIGNED_BYTE;
           bpp = 3;
         }
       else if (data_format == GDK_MEMORY_B8G8R8)
         {
+          gl_internalformat = GL_RGBA8;
           gl_format = GL_BGR;
           gl_type = GL_UNSIGNED_BYTE;
           bpp = 3;
         }
+      else if (data_format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED)
+        {
+          gl_internalformat = GL_RGBA16;
+          gl_format = GL_RGBA;
+          gl_type = GL_UNSIGNED_SHORT;
+          bpp = 8;
+        }
       else /* Fall-back, convert to cairo-surface-format */
         {
           copy = g_malloc (width * height * 4);
@@ -283,6 +295,7 @@ gdk_gl_context_upload_texture (GdkGLContext    *context,
           stride = width * 4;
           bpp = 4;
           data = copy;
+          gl_internalformat = GL_RGBA8;
           gl_format = GL_BGRA;
           gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
         }
@@ -295,7 +308,7 @@ gdk_gl_context_upload_texture (GdkGLContext    *context,
     {
       glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
 
-      glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
+      glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
       glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
     }
   else if ((!priv->use_es ||
@@ -303,14 +316,14 @@ gdk_gl_context_upload_texture (GdkGLContext    *context,
     {
       glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
 
-      glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
+      glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
 
       glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
     }
   else
     {
       int i;
-      glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, NULL);
+      glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
       for (i = 0; i < height; i++)
         glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
     }
diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c
index 06fa3d6619..fbb77851d9 100644
--- a/gdk/gdkmemorytexture.c
+++ b/gdk/gdkmemorytexture.c
@@ -62,6 +62,9 @@ gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format)
     case GDK_MEMORY_B8G8R8:
       return 3;
 
+    case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
+      return 8;
+
     case GDK_MEMORY_N_FORMATS:
     default:
       g_assert_not_reached ();
@@ -283,6 +286,37 @@ 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 SWIZZLE_16TO8(A,R,G,B) \
+static void \
+convert_16to8_swizzle_ ## A ## R ## G ## B (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++) \
+    { \
+      guint16 *src = (guint16 *)src_data; \
+      for (x = 0; x < width; x++) \
+        { \
+          dest_data[4 * x + A] = src[4 * x + 0] >> 8; \
+          dest_data[4 * x + R] = src[4 * x + 1] >> 8; \
+          dest_data[4 * x + G] = src[4 * x + 2] >> 8; \
+          dest_data[4 * x + B] = src[4 * x + 3] >> 8; \
+        } \
+\
+      dest_data += dest_stride; \
+      src_data += src_stride; \
+    } \
+}
+
+SWIZZLE_16TO8(0,1,2,3)
+SWIZZLE_16TO8(2,1,0,3)
+SWIZZLE_16TO8(1,2,3,0)
+
 typedef void (* ConversionFunc) (guchar       *dest_data,
                                  gsize         dest_stride,
                                  const guchar *src_data,
@@ -300,7 +334,8 @@ static ConversionFunc converters[GDK_MEMORY_N_FORMATS][3] =
   { 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_16to8_swizzle_2103, convert_16to8_swizzle_1230, convert_16to8_swizzle_0123 }
 };
 
 void
diff --git a/gdk/gdkmemorytexture.h b/gdk/gdkmemorytexture.h
index b9f1f21282..8fa3b22521 100644
--- a/gdk/gdkmemorytexture.h
+++ b/gdk/gdkmemorytexture.h
@@ -42,6 +42,8 @@ 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_R16G16B16A16_PREMULTIPLIED: 4 guint16 values; for red, green, blue, alpha.
+ *   The color values are premultiplied with the alpha value.
  * @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.
  *
@@ -53,6 +55,8 @@ G_BEGIN_DECLS
  * CAIRO_FORMAT_ARGB32 is represented by different `GdkMemoryFormats`
  * on architectures with different endiannesses.
  *
+ * Note that color data is assumed to be linear.
+ *
  * Its naming is modelled after
  * [VkFormat](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VkFormat)
  * for details).
@@ -67,6 +71,7 @@ typedef enum {
   GDK_MEMORY_A8B8G8R8,
   GDK_MEMORY_R8G8B8,
   GDK_MEMORY_B8G8R8,
+  GDK_MEMORY_R16G16B16A16_PREMULTIPLIED,
 
   GDK_MEMORY_N_FORMATS
 } GdkMemoryFormat;
diff --git a/testsuite/gdk/memorytexture.c b/testsuite/gdk/memorytexture.c
index ea24c827e3..00bb489b63 100644
--- a/testsuite/gdk/memorytexture.c
+++ b/testsuite/gdk/memorytexture.c
@@ -2,7 +2,7 @@
 #include <gdk/gdk.h>
 
 /* maximum bytes per pixel */
-#define MAX_BPP 4
+#define MAX_BPP 8
 
 typedef enum {
   BLUE,
@@ -33,6 +33,7 @@ typedef struct _TestData {
 } TestData;
 
 #define RGBA(a, b, c, d) { 0x ## a, 0x ## b, 0x ## c, 0x ## d }
+#define RGBA16(a, b, c, d) { 0x ## a, 0x ## a, 0x ## b, 0x ## b, 0x ## c, 0x ## c, 0x ## d, 0x ## d }
 
 static MemoryData tests[GDK_MEMORY_N_FORMATS] = {
   { 4, FALSE, { RGBA(FF,00,00,FF), RGBA(00,FF,00,FF), RGBA(00,00,FF,FF), RGBA(00,00,00,00), 
RGBA(66,22,44,AA) } },
@@ -44,12 +45,14 @@ static MemoryData tests[GDK_MEMORY_N_FORMATS] = {
   { 4, FALSE, { RGBA(FF,FF,00,00), RGBA(FF,00,FF,00), RGBA(FF,00,00,FF), RGBA(00,00,00,00), 
RGBA(AA,99,33,66) } },
   { 3, TRUE,  { RGBA(00,00,FF,00), RGBA(00,FF,00,00), RGBA(FF,00,00,00), RGBA(00,00,00,00), 
RGBA(44,22,66,00) } },
   { 3, TRUE,  { RGBA(FF,00,00,00), RGBA(00,FF,00,00), RGBA(00,00,FF,00), RGBA(00,00,00,00), 
RGBA(66,22,44,00) } },
+  { 8, FALSE, { RGBA16(00,00,FF,FF), RGBA16(00,FF,00,FF), RGBA16(FF,00,00,FF), RGBA16(00,00,00,00), 
RGBA16(44,22,66,AA) } },
 };
 
 static void
 compare_textures (GdkTexture *expected,
                   GdkTexture *test,
-                  gboolean    ignore_alpha)
+                  gboolean    ignore_alpha,
+                  int         bpp)
 {
   guchar *expected_data, *test_data;
   int width, height;
@@ -122,7 +125,7 @@ test_download_1x1 (gconstpointer data)
   expected = create_texture (GDK_MEMORY_DEFAULT, test_data->color, 1, 1, 
tests[test_data->format].bytes_per_pixel);
   test = create_texture (test_data->format, test_data->color, 1, 1, 
tests[test_data->format].bytes_per_pixel);
 
-  compare_textures (expected, test, tests[test_data->format].opaque);
+  compare_textures (expected, test, tests[test_data->format].opaque, 
tests[test_data->format].bytes_per_pixel);
 
   g_object_unref (expected);
   g_object_unref (test);
@@ -137,7 +140,7 @@ test_download_1x1_with_stride (gconstpointer data)
   expected = create_texture (GDK_MEMORY_DEFAULT, test_data->color, 1, 1, 4);
   test = create_texture (test_data->format, test_data->color, 1, 1, 2 * MAX_BPP);
 
-  compare_textures (expected, test, tests[test_data->format].opaque);
+  compare_textures (expected, test, tests[test_data->format].opaque, 
tests[test_data->format].bytes_per_pixel);
 
   g_object_unref (expected);
   g_object_unref (test);
@@ -152,7 +155,7 @@ test_download_4x4 (gconstpointer data)
   expected = create_texture (GDK_MEMORY_DEFAULT, test_data->color, 4, 4, 16);
   test = create_texture (test_data->format, test_data->color, 4, 4, 4 * 
tests[test_data->format].bytes_per_pixel);
 
-  compare_textures (expected, test, tests[test_data->format].opaque);
+  compare_textures (expected, test, tests[test_data->format].opaque, 
tests[test_data->format].bytes_per_pixel);
 
   g_object_unref (expected);
   g_object_unref (test);
@@ -167,7 +170,7 @@ test_download_4x4_with_stride (gconstpointer data)
   expected = create_texture (GDK_MEMORY_DEFAULT, test_data->color, 4, 4, 16);
   test = create_texture (test_data->format, test_data->color, 4, 4, 4 * MAX_BPP);
 
-  compare_textures (expected, test, tests[test_data->format].opaque);
+  compare_textures (expected, test, tests[test_data->format].opaque, 
tests[test_data->format].bytes_per_pixel);
 
   g_object_unref (expected);
   g_object_unref (test);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]