[gtk/gamma-shenanigans: 5/11] Save pngs using libpng




commit 15cf326f910f869e5bd89b22d569de2495cf2e92
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Sep 5 23:18:59 2021 -0400

    Save pngs using libpng
    
    If we have a texture with 16bit data, lets save
    it as such when gdk_texture_save_to_png is called.
    libpng lets us do this.
    
    This replaces the cairo saving code with an
    implementation using libpng's png_image.

 gdk/gdkgltexture.c      | 31 ++++++++++++++++++++++++++
 gdk/gdkmemorytexture.c  | 12 ++++++++++
 gdk/gdktexture.c        | 59 +++++++++++++++++++++++++++++++++++--------------
 gdk/gdktextureprivate.h |  3 +++
 4 files changed, 89 insertions(+), 16 deletions(-)
---
diff --git a/gdk/gdkgltexture.c b/gdk/gdkgltexture.c
index caf3e7ab6b..974545f576 100644
--- a/gdk/gdkgltexture.c
+++ b/gdk/gdkgltexture.c
@@ -22,6 +22,7 @@
 
 #include "gdkcairo.h"
 #include "gdktextureprivate.h"
+#include "gdkinternals.h"
 
 #include <epoxy/gl.h>
 
@@ -110,6 +111,35 @@ gdk_gl_texture_download (GdkTexture         *texture,
   cairo_surface_destroy (surface);
 }
 
+static GBytes *
+gdk_gl_texture_download_16bit (GdkTexture *texture)
+{
+  GdkGLTexture *self = GDK_GL_TEXTURE (texture);
+  GdkSurface *surface;
+  GdkGLContext *context;
+  int format = 0;
+  gpointer data;
+  gsize size;
+
+  surface = gdk_gl_context_get_surface (self->context);
+  context = gdk_surface_get_paint_gl_context (surface, NULL);
+
+  gdk_gl_context_make_current (context);
+  glBindTexture (GL_TEXTURE_2D, self->id);
+  glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
+
+  if (format != GL_RGBA16)
+    return NULL;
+
+  size = texture->width * texture->height * 4 * sizeof (guint16);
+  data = malloc (size);
+
+  glReadPixels (0, 0, texture->width, texture->height,
+                GL_RGBA16, GL_UNSIGNED_SHORT, data);
+
+  return g_bytes_new_take (data, size);
+}
+
 static void
 gdk_gl_texture_class_init (GdkGLTextureClass *klass)
 {
@@ -117,6 +147,7 @@ gdk_gl_texture_class_init (GdkGLTextureClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   texture_class->download = gdk_gl_texture_download;
+  texture_class->download_16bit = gdk_gl_texture_download_16bit;
   gobject_class->dispose = gdk_gl_texture_dispose;
 }
 
diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c
index fbb77851d9..69d0e0317e 100644
--- a/gdk/gdkmemorytexture.c
+++ b/gdk/gdkmemorytexture.c
@@ -100,6 +100,17 @@ gdk_memory_texture_download (GdkTexture         *texture,
                       area->width, area->height);
 }
 
+static GBytes *
+gdk_memory_texture_download_16bit (GdkTexture *texture)
+{
+  GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
+
+  if (self->format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED)
+    return g_bytes_ref (self->bytes);
+
+  return NULL;
+}
+
 static void
 gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
 {
@@ -107,6 +118,7 @@ gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   texture_class->download = gdk_memory_texture_download;
+  texture_class->download_16bit = gdk_memory_texture_download_16bit;
   gobject_class->dispose = gdk_memory_texture_dispose;
 }
 
diff --git a/gdk/gdktexture.c b/gdk/gdktexture.c
index b33a93c38d..e158b62068 100644
--- a/gdk/gdktexture.c
+++ b/gdk/gdktexture.c
@@ -125,6 +125,12 @@ gdk_texture_real_download (GdkTexture         *self,
   GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download);
 }
 
+static GBytes *
+gdk_texture_real_download_16bit (GdkTexture *self)
+{
+  return NULL;
+}
+
 static void
 gdk_texture_set_property (GObject      *gobject,
                           guint         prop_id,
@@ -189,6 +195,7 @@ gdk_texture_class_init (GdkTextureClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   klass->download = gdk_texture_real_download;
+  klass->download_16bit = gdk_texture_real_download_16bit;
 
   gobject_class->set_property = gdk_texture_set_property;
   gobject_class->get_property = gdk_texture_get_property;
@@ -564,6 +571,12 @@ gdk_texture_download (GdkTexture *texture,
                              stride);
 }
 
+GBytes *
+gdk_texture_download_16bit (GdkTexture *texture)
+{
+  return GDK_TEXTURE_GET_CLASS (texture)->download_16bit (texture);
+}
+
 gboolean
 gdk_texture_set_render_data (GdkTexture     *self,
                              gpointer        key,
@@ -610,6 +623,9 @@ gdk_texture_get_render_data (GdkTexture  *self,
  *
  * Store the given @texture to the @filename as a PNG file.
  *
+ * If the texture contains 16bit data, the generated PNG file
+ * will have linear 16bit data, otherwise it will contain SRGB.
+ *
  * This is a utility function intended for debugging and testing.
  * If you want more control over formats, proper error handling or
  * want to store to a `GFile` or other location, you might want to
@@ -621,30 +637,41 @@ gboolean
 gdk_texture_save_to_png (GdkTexture *texture,
                          const char *filename)
 {
-  cairo_surface_t *surface;
-  cairo_status_t status;
+  png_image image = { NULL, PNG_IMAGE_VERSION, 0, };
+  GBytes *bytes;
   gboolean result;
 
   g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
   g_return_val_if_fail (filename != NULL, FALSE);
 
-  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                        gdk_texture_get_width (texture),
-                                        gdk_texture_get_height (texture));
-  gdk_texture_download (texture,
-                        cairo_image_surface_get_data (surface),
-                        cairo_image_surface_get_stride (surface));
-  cairo_surface_mark_dirty (surface);
-
-  status = cairo_surface_write_to_png (surface, filename);
+  image.width = gdk_texture_get_width (texture);
+  image.height = gdk_texture_get_height (texture);
 
-  if (status != CAIRO_STATUS_SUCCESS ||
-      cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
-    result = FALSE;
+  bytes = gdk_texture_download_16bit (texture);
+  if (bytes)
+    {
+      image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
+    }
   else
-    result = TRUE;
+    {
+      int stride = image.width * 4;
+      gpointer data;
+
+      data = g_malloc (image.height * stride);
+
+      gdk_texture_download (texture, data, stride);
 
-  cairo_surface_destroy (surface);
+      bytes = g_bytes_new_take (data, image.height * stride);
+
+      image.format = PNG_FORMAT_RGBA;
+    }
+
+  result = png_image_write_to_file (&image, filename, FALSE,
+                                    g_bytes_get_data (bytes, NULL),
+                                    0, NULL);
+  g_bytes_unref (bytes);
+
+  png_image_free (&image);
 
   return result;
 }
diff --git a/gdk/gdktextureprivate.h b/gdk/gdktextureprivate.h
index 3e2e9f3a49..e0b7b842a6 100644
--- a/gdk/gdktextureprivate.h
+++ b/gdk/gdktextureprivate.h
@@ -28,6 +28,7 @@ struct _GdkTextureClass {
                                                          const GdkRectangle     *area,
                                                          guchar                 *data,
                                                          gsize                   stride);
+  GBytes *              (* download_16bit)              (GdkTexture             *texture);
 };
 
 gpointer                gdk_texture_new                 (const GdkTextureClass  *klass,
@@ -48,6 +49,8 @@ void                    gdk_texture_clear_render_data   (GdkTexture
 gpointer                gdk_texture_get_render_data     (GdkTexture             *self,
                                                          gpointer                key);
 
+GBytes *                gdk_texture_download_16bit      (GdkTexture             *texture);
+
 G_END_DECLS
 
 #endif /* __GDK_TEXTURE_PRIVATE_H__ */


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