[gtk/gamma-shenanigans: 23/29] png: Handle all formats when saving




commit 5a9c81ab6fe9ae7bedc29d43a0e7c94557f1c4e9
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Sep 10 12:20:06 2021 -0400

    png: Handle all formats when saving
    
    The serialization framework expects us to handle any
    data it throws at us, so lets make an effort.

 gdk/gdkpng.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 156 insertions(+), 9 deletions(-)
---
diff --git a/gdk/gdkpng.c b/gdk/gdkpng.c
index 34e801cff1..17d1a2b899 100644
--- a/gdk/gdkpng.c
+++ b/gdk/gdkpng.c
@@ -21,6 +21,7 @@
 
 #include "gdktexture.h"
 #include "gdkmemorytextureprivate.h"
+#include "gsk/ngl/fp16private.h"
 #include <png.h>
 #include <stdio.h>
 
@@ -118,6 +119,118 @@ read_all_data (GInputStream  *source,
 
 #endif
 
+/* }}} */
+/* {{{ Format conversion */
+
+static void
+convert_half_float (guchar            *dest_data,
+                    gsize              dest_stride,
+                    GdkMemoryFormat    dest_format,
+                    const guchar      *src_data,
+                    gsize              src_stride,
+                    GdkMemoryFormat    src_format,
+                    gsize              width,
+                    gsize              height)
+{
+  gsize x, y;
+  guint16 *dest;
+  const guint16 *src;
+  float *c;
+
+  c = g_malloc (width * 4 * sizeof (float));
+  for (y = 0; y < height; y++)
+    {
+      dest = (guint16 *)dest_data;
+      src = (const guint16 *)src_data;
+
+      half_to_float (src, c, width);
+      for (x = 0; x < width; x++)
+        {
+          dest[4 * x    ] = (guint16)(65535 * c[4 * x    ]);
+          dest[4 * x + 1] = (guint16)(65535 * c[4 * x + 1]);
+          dest[4 * x + 2] = (guint16)(65535 * c[4 * x + 2]);
+          if (src_format == GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED)
+            dest[4 * x + 3] = (guint16)(65535 * c[4 * x + 3]);
+          else
+            dest[4 * x + 3] = 65535;
+        }
+      dest_data += dest_stride;
+      src_data += src_stride;
+    }
+  g_free (c);
+}
+
+static void
+convert_float (guchar            *dest_data,
+               gsize              dest_stride,
+               GdkMemoryFormat    dest_format,
+               const guchar      *src_data,
+               gsize              src_stride,
+               GdkMemoryFormat    src_format,
+               gsize              width,
+               gsize              height)
+{
+  gsize x, y;
+  guint16 *dest;
+  const float *src;
+
+  for (y = 0; y < height; y++)
+    {
+      dest = (guint16 *)dest_data;
+      src = (const float *)src_data;
+      for (x = 0; x < width; x++)
+        {
+          dest[4 * x    ] = (guint16)(65535 * src[4 * x    ]);
+          dest[4 * x + 1] = (guint16)(65535 * src[4 * x + 1]);
+          dest[4 * x + 2] = (guint16)(65535 * src[4 * x + 2]);
+          if (src_format == GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED)
+            dest[4 * x + 3] = (guint16)(65535 * src[4 * x + 3]);
+          else
+            dest[4 * x + 3] = 65535;
+        }
+      dest_data += dest_stride;
+      src_data += src_stride;
+    }
+}
+
+static void
+convert (guchar            *dest_data,
+         gsize              dest_stride,
+         GdkMemoryFormat    dest_format,
+         const guchar      *src_data,
+         gsize              src_stride,
+         GdkMemoryFormat    src_format,
+         gsize              width,
+         gsize              height)
+{
+  if (dest_format < 3)
+    gdk_memory_convert (dest_data, dest_stride, dest_format,
+                        src_data, src_stride, src_format,
+                        width, height);
+  else
+    {
+      g_assert (dest_format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED);
+
+      switch ((int)src_format)
+        {
+        case GDK_MEMORY_R16G16B16_FLOAT:
+        case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
+          convert_half_float (dest_data, dest_stride, dest_format,
+                              src_data, src_stride, src_format,
+                              width, height);
+          break;
+        case GDK_MEMORY_R32G32B32_FLOAT:
+        case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
+          convert_float (dest_data, dest_stride, dest_format,
+                         src_data, src_stride, src_format,
+                         width, height);
+          break;
+        default:
+          g_assert_not_reached ();
+        }
+    }
+}
+
 /* }}} */
 /* {{{ Public API */
 
@@ -185,17 +298,49 @@ gdk_save_png (GOutputStream    *stream,
 {
   png_image image = { NULL, PNG_IMAGE_VERSION, 0, };
   gboolean result;
+  guchar *new_data = NULL;
 
-  if (format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED)
-    image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
-  else if (format == GDK_MEMORY_DEFAULT)
-    image.format = PNG_FORMAT_RGBA;
-  else
+  switch ((int)format)
     {
-      g_set_error (error,
-                   G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Saving memory format %d to png not implemented", format);
-      return FALSE;
+    case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
+      image.format = PNG_FORMAT_RGBA;
+      break;
+    case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
+    case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
+    case GDK_MEMORY_B8G8R8A8:
+    case GDK_MEMORY_A8R8G8B8:
+    case GDK_MEMORY_R8G8B8A8:
+    case GDK_MEMORY_A8B8G8R8:
+    case GDK_MEMORY_R8G8B8:
+    case GDK_MEMORY_B8G8R8:
+      stride = width * 4;
+      new_data = g_malloc (stride * height);
+      convert (new_data, stride, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
+               data, width * gdk_memory_format_bytes_per_pixel (format), format,
+               width, height);
+      data = new_data;
+      image.format = PNG_FORMAT_RGBA;
+      break;
+    case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
+      image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+      break;
+    case GDK_MEMORY_R16G16B16:
+      image.format = PNG_FORMAT_LINEAR_RGB;
+      break;
+    case GDK_MEMORY_R16G16B16_FLOAT:
+    case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
+    case GDK_MEMORY_R32G32B32_FLOAT:
+    case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
+      stride = width * 8;
+      new_data = g_malloc (stride * height);
+      convert (new_data, stride, GDK_MEMORY_R16G16B16A16_PREMULTIPLIED,
+               data, width * gdk_memory_format_bytes_per_pixel (format), format,
+               width, height);
+      image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+      break;
+      break;
+    default:
+      g_assert_not_reached ();
     }
 
   if (image.format & PNG_FORMAT_FLAG_LINEAR)
@@ -226,6 +371,8 @@ gdk_save_png (GOutputStream    *stream,
 
   png_image_free (&image);
 
+  g_free (new_data);
+
   return result;
 }
 


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