From c2368cc60527b24fabb962e71472671425a6eb0f Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 27 Sep 2021 05:55:48 +0200 Subject: [PATCH] png: Refactor png saving Do all the memory format shenanigans in GTK now and support all the PNG formats. --- gdk/loaders/gdkpng.c | 141 ++++++++++++------------------------------- 1 file changed, 37 insertions(+), 104 deletions(-) diff --git a/gdk/loaders/gdkpng.c b/gdk/loaders/gdkpng.c index 526c6fa50f..11ed7e4ef9 100644 --- a/gdk/loaders/gdkpng.c +++ b/gdk/loaders/gdkpng.c @@ -127,85 +127,6 @@ png_simple_warning_callback (png_structp png, { } -/* }}} */ -/* {{{ Format conversion */ - -static void -unpremultiply (guchar *data, - int width, - int height) -{ - gsize x, y; - - for (y = 0; y < height; y++) - { - for (x = 0; x < width; x++) - { - guchar *b = &data[x * 4]; - guint32 pixel; - guchar alpha; - - memcpy (&pixel, b, sizeof (guint32)); - alpha = (pixel & 0xff000000) >> 24; - if (alpha == 0) - { - b[0] = 0; - b[1] = 0; - b[2] = 0; - b[3] = 0; - } - else - { - b[0] = (((pixel & 0x00ff0000) >> 16) * 255 + alpha / 2) / alpha; - b[1] = (((pixel & 0x0000ff00) >> 8) * 255 + alpha / 2) / alpha; - b[2] = (((pixel & 0x000000ff) >> 0) * 255 + alpha / 2) / alpha; - b[3] = alpha; - } - } - data += width * 4; - } -} - -static void -unpremultiply_float_to_16bit (guchar *data, - int width, - int height) -{ - gsize x, y; - float *src = (float *)data;; - guint16 *dest = (guint16 *)data; - - for (y = 0; y < height; y++) - { - for (x = 0; x < width; x++) - { - float r, g, b, a; - - r = src[0]; - g = src[1]; - b = src[2]; - a = src[3]; - if (a == 0) - { - dest[0] = 0; - dest[1] = 0; - dest[2] = 0; - dest[3] = 0; - } - else - { - dest[0] = (guint16) CLAMP (65536.f * r / a, 0.f, 65535.f); - dest[1] = (guint16) CLAMP (65536.f * g / a, 0.f, 65535.f); - dest[2] = (guint16) CLAMP (65536.f * b / a, 0.f, 65535.f); - dest[3] = (guint16) CLAMP (65536.f * a, 0.f, 65535.f); - } - - dest += 4; - src += 4; - } - } -} - /* }}} */ /* {{{ Public API */ @@ -382,11 +303,11 @@ gdk_save_png (GdkTexture *texture) png_struct *png = NULL; png_info *info; png_io io = { NULL, 0, 0 }; - guint width, height, stride; - guchar *data = NULL; - guchar *row; + int width, height; + gsize stride; + const guchar *data; int y; - GdkTexture *mtexture; + GdkMemoryTexture *memtex; GdkMemoryFormat format; int png_format; int depth; @@ -395,8 +316,6 @@ gdk_save_png (GdkTexture *texture) height = gdk_texture_get_height (texture); format = gdk_texture_get_format (texture); - mtexture = GDK_TEXTURE (gdk_memory_texture_from_texture (texture, format)); - switch (format) { case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED: @@ -406,32 +325,42 @@ gdk_save_png (GdkTexture *texture) case GDK_MEMORY_A8R8G8B8: case GDK_MEMORY_R8G8B8A8: case GDK_MEMORY_A8B8G8R8: - case GDK_MEMORY_R8G8B8: - case GDK_MEMORY_B8G8R8: - stride = width * 4; - data = g_malloc_n (stride, height); - gdk_texture_download (mtexture, data, stride); - unpremultiply (data, width, height); - +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + format = GDK_MEMORY_R8G8B8A8; +#elif G_BYTE_ORDER == G_BIG_ENDIAN + format = GDK_MEMORY_A8B8G8R8; +#endif png_format = PNG_COLOR_TYPE_RGB_ALPHA; depth = 8; break; - case GDK_MEMORY_R16G16B16: + case GDK_MEMORY_R8G8B8: + case GDK_MEMORY_B8G8R8: +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + format = GDK_MEMORY_R8G8B8; +#elif G_BYTE_ORDER == G_BIG_ENDIAN + format = GDK_MEMORY_B8G8R8; +#endif + png_format = PNG_COLOR_TYPE_RGB; + depth = 8; + break; + case GDK_MEMORY_R16G16B16A16: case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: - case GDK_MEMORY_R16G16B16_FLOAT: case GDK_MEMORY_R16G16B16A16_FLOAT: case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: - case GDK_MEMORY_R32G32B32_FLOAT: case GDK_MEMORY_R32G32B32A32_FLOAT: case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: - data = g_malloc_n (width * 16, height); - gdk_texture_download_float (mtexture, (float *)data, width * 4); - unpremultiply_float_to_16bit (data, width, height); - + format = GDK_MEMORY_R16G16B16A16; png_format = PNG_COLOR_TYPE_RGB_ALPHA; - stride = width * 8; + depth = 16; + break; + + case GDK_MEMORY_R16G16B16: + case GDK_MEMORY_R16G16B16_FLOAT: + case GDK_MEMORY_R32G32B32_FLOAT: + format = GDK_MEMORY_R16G16B16; + png_format = PNG_COLOR_TYPE_RGB; depth = 16; break; @@ -458,12 +387,14 @@ gdk_save_png (GdkTexture *texture) if (sigsetjmp (png_jmpbuf (png), 1)) { - g_free (data); + g_object_unref (memtex); g_free (io.data); png_destroy_read_struct (&png, &info, NULL); return NULL; } + memtex = gdk_memory_texture_from_texture (texture, format); + png_set_write_fn (png, &io, png_write_func, png_flush_func); png_set_IHDR (png, info, width, height, depth, @@ -478,14 +409,16 @@ gdk_save_png (GdkTexture *texture) png_set_swap (png); #endif - for (y = 0, row = data; y < height; y++, row += stride) - png_write_rows (png, &row, 1); + data = gdk_memory_texture_get_data (memtex); + stride = gdk_memory_texture_get_stride (memtex); + for (y = 0; y < height; y++) + png_write_row (png, data + y * stride); png_write_end (png, info); png_destroy_write_struct (&png, &info); - g_free (data); + g_object_unref (memtex); return g_bytes_new_take (io.data, io.size); }