2021-09-10 16:44:43 +00:00
|
|
|
/* GDK - The GIMP Drawing Kit
|
|
|
|
* Copyright (C) 2021 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "gdkpngprivate.h"
|
|
|
|
|
2022-09-24 03:33:42 +00:00
|
|
|
#include <glib/gi18n-lib.h>
|
2024-07-16 15:48:50 +00:00
|
|
|
#include "gdkcolorstateprivate.h"
|
2021-09-22 00:01:41 +00:00
|
|
|
#include "gdkmemoryformatprivate.h"
|
2022-05-08 14:12:59 +00:00
|
|
|
#include "gdkmemorytexturebuilder.h"
|
2021-09-18 18:12:39 +00:00
|
|
|
#include "gdkprofilerprivate.h"
|
2023-02-14 06:19:18 +00:00
|
|
|
#include "gdktexturedownloaderprivate.h"
|
2021-10-07 03:15:25 +00:00
|
|
|
#include "gsk/gl/fp16private.h"
|
2024-07-16 15:48:50 +00:00
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
#include <png.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
/* The main difference between the png load/save code here and
|
|
|
|
* gdk-pixbuf is that we can support loading 16-bit data in the
|
|
|
|
* future, and we can extract gamma and colorspace information
|
|
|
|
* to produce linear, color-corrected data.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* {{{ Callbacks */
|
|
|
|
|
|
|
|
/* No sigsetjmp on Windows */
|
|
|
|
#ifndef HAVE_SIGSETJMP
|
|
|
|
#define sigjmp_buf jmp_buf
|
|
|
|
#define sigsetjmp(jb, x) setjmp(jb)
|
|
|
|
#define siglongjmp longjmp
|
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
guchar *data;
|
|
|
|
gsize size;
|
|
|
|
gsize position;
|
|
|
|
} png_io;
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
png_read_func (png_structp png,
|
|
|
|
png_bytep data,
|
|
|
|
png_size_t size)
|
|
|
|
{
|
|
|
|
png_io *io;
|
|
|
|
|
|
|
|
io = png_get_io_ptr (png);
|
|
|
|
|
|
|
|
if (io->position + size > io->size)
|
|
|
|
png_error (png, "Read past EOF");
|
|
|
|
|
|
|
|
memcpy (data, io->data + io->position, size);
|
|
|
|
io->position += size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
png_write_func (png_structp png,
|
|
|
|
png_bytep data,
|
|
|
|
png_size_t size)
|
|
|
|
{
|
|
|
|
png_io *io;
|
|
|
|
|
|
|
|
io = png_get_io_ptr (png);
|
|
|
|
|
|
|
|
if (io->position > io->size ||
|
|
|
|
io->size - io->position < size)
|
|
|
|
{
|
|
|
|
io->size = io->position + size;
|
|
|
|
io->data = g_realloc (io->data, io->size);
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy (io->data + io->position, data, size);
|
|
|
|
io->position += size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
png_flush_func (png_structp png)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static png_voidp
|
|
|
|
png_malloc_callback (png_structp o,
|
|
|
|
png_size_t size)
|
|
|
|
{
|
|
|
|
return g_try_malloc (size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
png_free_callback (png_structp o,
|
|
|
|
png_voidp x)
|
|
|
|
{
|
|
|
|
g_free (x);
|
|
|
|
}
|
|
|
|
|
2021-09-19 09:00:07 +00:00
|
|
|
G_GNUC_NORETURN static void
|
2021-09-10 16:44:43 +00:00
|
|
|
png_simple_error_callback (png_structp png,
|
|
|
|
png_const_charp error_msg)
|
|
|
|
{
|
|
|
|
GError **error = png_get_error_ptr (png);
|
|
|
|
|
|
|
|
if (error && !*error)
|
|
|
|
g_set_error (error,
|
|
|
|
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_CORRUPT_IMAGE,
|
2021-09-17 01:25:35 +00:00
|
|
|
_("Error reading png (%s)"), error_msg);
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
longjmp (png_jmpbuf (png), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
png_simple_warning_callback (png_structp png,
|
|
|
|
png_const_charp error_msg)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-07-16 15:48:50 +00:00
|
|
|
/* }}} */
|
|
|
|
/* {{{ Color profile handling */
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gboolean cicp_chunk_read;
|
|
|
|
int color_primaries;
|
|
|
|
int transfer_function;
|
|
|
|
int matrix_coefficients;
|
2024-07-22 13:41:32 +00:00
|
|
|
int range;
|
2024-07-16 15:48:50 +00:00
|
|
|
} CICPData;
|
|
|
|
|
|
|
|
static int
|
|
|
|
png_read_chunk_func (png_structp png,
|
|
|
|
png_unknown_chunkp chunk)
|
|
|
|
{
|
|
|
|
if (strcmp ((char *) chunk->name, "cICP") == 0 &&
|
|
|
|
chunk->size == 4)
|
|
|
|
{
|
|
|
|
CICPData *cicp = png_get_user_chunk_ptr (png);
|
|
|
|
|
|
|
|
cicp->cicp_chunk_read = TRUE;
|
|
|
|
|
|
|
|
cicp->color_primaries = chunk->data[0];
|
|
|
|
cicp->transfer_function = chunk->data[1];
|
|
|
|
cicp->matrix_coefficients = chunk->data[2];
|
2024-07-22 13:41:32 +00:00
|
|
|
cicp->range = chunk->data[3];
|
2024-07-16 15:48:50 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GdkColorState *
|
2024-07-22 13:41:32 +00:00
|
|
|
gdk_png_get_color_state_from_cicp (const CICPData *data,
|
|
|
|
GError **error)
|
2024-07-16 15:48:50 +00:00
|
|
|
{
|
2024-07-22 13:41:32 +00:00
|
|
|
GdkCicp cicp;
|
2024-07-16 15:48:50 +00:00
|
|
|
|
2024-07-22 13:41:32 +00:00
|
|
|
cicp.color_primaries = data->color_primaries;
|
|
|
|
cicp.transfer_function = data->transfer_function;
|
|
|
|
cicp.matrix_coefficients= data->matrix_coefficients;
|
|
|
|
cicp.range = data->range;
|
2024-07-16 15:48:50 +00:00
|
|
|
|
2024-07-22 13:41:32 +00:00
|
|
|
return gdk_color_state_new_for_cicp (&cicp, error);
|
2024-07-16 15:48:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static GdkColorState *
|
|
|
|
gdk_png_get_color_state (png_struct *png,
|
|
|
|
png_info *info,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
GdkColorState *color_state;
|
|
|
|
CICPData *cicp;
|
|
|
|
int intent;
|
|
|
|
|
|
|
|
cicp = png_get_user_chunk_ptr (png);
|
|
|
|
|
|
|
|
if (cicp->cicp_chunk_read)
|
|
|
|
{
|
2024-07-22 13:41:32 +00:00
|
|
|
GError *local_error = NULL;
|
|
|
|
|
|
|
|
color_state = gdk_png_get_color_state_from_cicp (cicp, &local_error);
|
2024-07-16 15:48:50 +00:00
|
|
|
if (color_state)
|
|
|
|
{
|
|
|
|
g_debug ("Color state from cICP data: %s", gdk_color_state_get_name (color_state));
|
|
|
|
return color_state;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-07-22 13:41:32 +00:00
|
|
|
g_set_error_literal (error,
|
|
|
|
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
|
|
|
|
local_error->message);
|
|
|
|
g_error_free (local_error);
|
2024-07-16 15:48:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (png_get_iCCP (png, info, &name, NULL, &icc_data, &icc_len))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (png_get_sRGB (png, info, &intent))
|
|
|
|
return gdk_color_state_ref (GDK_COLOR_STATE_SRGB);
|
|
|
|
|
|
|
|
/* If neither of those is valid, the result is sRGB */
|
|
|
|
if (!png_get_valid (png, info, PNG_INFO_gAMA) &&
|
|
|
|
!png_get_valid (png, info, PNG_INFO_cHRM))
|
|
|
|
return GDK_COLOR_STATE_SRGB;
|
|
|
|
|
|
|
|
g_debug ("Failed to find color state, assuming SRGB");
|
|
|
|
|
|
|
|
return GDK_COLOR_STATE_SRGB;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gdk_png_set_color_state (png_struct *png,
|
|
|
|
png_info *info,
|
|
|
|
GdkColorState *color_state,
|
|
|
|
png_byte *chunk_data)
|
|
|
|
{
|
2024-07-22 13:41:32 +00:00
|
|
|
const GdkCicp *cicp;
|
2024-07-16 15:48:50 +00:00
|
|
|
|
2024-07-22 13:41:32 +00:00
|
|
|
cicp = gdk_color_state_get_cicp (color_state);
|
2024-07-16 15:48:50 +00:00
|
|
|
|
2024-07-22 13:41:32 +00:00
|
|
|
if (cicp)
|
2024-07-16 15:48:50 +00:00
|
|
|
{
|
|
|
|
png_unknown_chunk chunk = {
|
|
|
|
.name = { 'c', 'I', 'C', 'P', '\0' },
|
|
|
|
.data = chunk_data,
|
|
|
|
.size = 4,
|
|
|
|
.location = PNG_HAVE_IHDR,
|
|
|
|
};
|
|
|
|
|
|
|
|
chunk_data[0] = (png_byte) cicp->color_primaries;
|
|
|
|
chunk_data[1] = (png_byte) cicp->transfer_function;
|
2024-07-22 13:41:32 +00:00
|
|
|
chunk_data[2] = (png_byte) 0; /* png only supports this */
|
|
|
|
chunk_data[3] = (png_byte) cicp->range;
|
2024-07-16 15:48:50 +00:00
|
|
|
|
|
|
|
png_set_unknown_chunks (png, info, &chunk, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* unsupported color state. Fall back to sRGB */
|
|
|
|
gdk_color_state_unref (color_state);
|
|
|
|
color_state = gdk_color_state_ref (gdk_color_state_get_srgb ());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For good measure, we add an sRGB chunk too */
|
|
|
|
if (gdk_color_state_equal (color_state, GDK_COLOR_STATE_SRGB))
|
|
|
|
png_set_sRGB (png, info, PNG_sRGB_INTENT_PERCEPTUAL);
|
|
|
|
}
|
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
/* }}} */
|
2024-04-28 19:07:25 +00:00
|
|
|
/* {{{ Public API */
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
GdkTexture *
|
2024-04-28 19:07:25 +00:00
|
|
|
gdk_load_png (GBytes *bytes,
|
|
|
|
GHashTable *options,
|
|
|
|
GError **error)
|
2021-09-10 16:44:43 +00:00
|
|
|
{
|
|
|
|
png_io io;
|
|
|
|
png_struct *png = NULL;
|
|
|
|
png_info *info;
|
2024-04-28 19:07:25 +00:00
|
|
|
png_textp text;
|
|
|
|
int num_texts;
|
2021-09-10 16:44:43 +00:00
|
|
|
guint width, height;
|
2024-01-01 23:54:36 +00:00
|
|
|
gsize i, stride;
|
2021-09-10 16:44:43 +00:00
|
|
|
int depth, color_type;
|
2024-01-01 23:54:36 +00:00
|
|
|
int interlace;
|
2022-05-08 14:12:59 +00:00
|
|
|
GdkMemoryTextureBuilder *builder;
|
2021-09-10 16:44:43 +00:00
|
|
|
GdkMemoryFormat format;
|
|
|
|
guchar *buffer = NULL;
|
|
|
|
guchar **row_pointers = NULL;
|
|
|
|
GBytes *out_bytes;
|
2024-07-16 15:48:50 +00:00
|
|
|
GdkColorState *color_state;
|
2021-09-10 16:44:43 +00:00
|
|
|
GdkTexture *texture;
|
2021-09-15 04:41:40 +00:00
|
|
|
int bpp;
|
2024-07-16 15:48:50 +00:00
|
|
|
CICPData cicp = { FALSE, };
|
|
|
|
|
2021-09-18 18:12:39 +00:00
|
|
|
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
io.data = (guchar *)g_bytes_get_data (bytes, &io.size);
|
|
|
|
io.position = 0;
|
|
|
|
|
|
|
|
png = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
|
|
|
|
error,
|
|
|
|
png_simple_error_callback,
|
|
|
|
png_simple_warning_callback,
|
|
|
|
NULL,
|
|
|
|
png_malloc_callback,
|
|
|
|
png_free_callback);
|
|
|
|
if (png == NULL)
|
2021-09-17 01:25:35 +00:00
|
|
|
g_error ("Out of memory");
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
info = png_create_info_struct (png);
|
|
|
|
if (info == NULL)
|
2021-09-17 01:25:35 +00:00
|
|
|
g_error ("Out of memory");
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
png_set_read_fn (png, &io, png_read_func);
|
2024-07-16 15:48:50 +00:00
|
|
|
png_set_read_user_chunk_fn (png, &cicp, png_read_chunk_func);
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
if (sigsetjmp (png_jmpbuf (png), 1))
|
|
|
|
{
|
|
|
|
g_free (buffer);
|
|
|
|
g_free (row_pointers);
|
|
|
|
png_destroy_read_struct (&png, &info, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
png_read_info (png, info);
|
|
|
|
|
|
|
|
png_get_IHDR (png, info,
|
|
|
|
&width, &height, &depth,
|
|
|
|
&color_type, &interlace, NULL, NULL);
|
|
|
|
|
|
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
|
|
png_set_palette_to_rgb (png);
|
|
|
|
|
2023-05-26 13:35:57 +00:00
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY)
|
2021-09-10 16:44:43 +00:00
|
|
|
png_set_expand_gray_1_2_4_to_8 (png);
|
|
|
|
|
|
|
|
if (png_get_valid (png, info, PNG_INFO_tRNS))
|
|
|
|
png_set_tRNS_to_alpha (png);
|
|
|
|
|
|
|
|
if (depth < 8)
|
|
|
|
png_set_packing (png);
|
|
|
|
|
|
|
|
if (interlace != PNG_INTERLACE_NONE)
|
|
|
|
png_set_interlace_handling (png);
|
|
|
|
|
2021-09-26 04:44:56 +00:00
|
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
|
|
png_set_swap (png);
|
|
|
|
#endif
|
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
png_read_update_info (png, info);
|
|
|
|
png_get_IHDR (png, info,
|
|
|
|
&width, &height, &depth,
|
|
|
|
&color_type, &interlace, NULL, NULL);
|
2021-09-26 04:44:56 +00:00
|
|
|
if (depth != 8 && depth != 16)
|
2021-09-10 16:44:43 +00:00
|
|
|
{
|
|
|
|
png_destroy_read_struct (&png, &info, NULL);
|
|
|
|
g_set_error (error,
|
2021-09-17 01:25:35 +00:00
|
|
|
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
|
2021-09-26 04:44:56 +00:00
|
|
|
_("Unsupported depth %u in png image"), depth);
|
2021-09-10 16:44:43 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (color_type)
|
|
|
|
{
|
|
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
2021-09-15 04:41:40 +00:00
|
|
|
if (depth == 8)
|
|
|
|
{
|
2021-09-26 04:44:56 +00:00
|
|
|
format = GDK_MEMORY_R8G8B8A8;
|
2021-09-15 04:41:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-26 14:47:34 +00:00
|
|
|
format = GDK_MEMORY_R16G16B16A16;
|
2021-09-15 04:41:40 +00:00
|
|
|
}
|
2021-09-10 16:44:43 +00:00
|
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_RGB:
|
2021-09-15 04:41:40 +00:00
|
|
|
if (depth == 8)
|
|
|
|
{
|
2021-09-26 04:44:56 +00:00
|
|
|
format = GDK_MEMORY_R8G8B8;
|
2021-09-15 04:41:40 +00:00
|
|
|
}
|
2021-09-26 04:44:56 +00:00
|
|
|
else if (depth == 16)
|
2021-09-15 04:41:40 +00:00
|
|
|
{
|
|
|
|
format = GDK_MEMORY_R16G16B16;
|
|
|
|
}
|
2021-09-10 16:44:43 +00:00
|
|
|
break;
|
2023-05-26 13:35:57 +00:00
|
|
|
case PNG_COLOR_TYPE_GRAY:
|
|
|
|
if (depth == 8)
|
|
|
|
{
|
|
|
|
format = GDK_MEMORY_G8;
|
|
|
|
}
|
|
|
|
else if (depth == 16)
|
|
|
|
{
|
|
|
|
format = GDK_MEMORY_G16;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
|
|
|
if (depth == 8)
|
|
|
|
{
|
|
|
|
format = GDK_MEMORY_G8A8;
|
|
|
|
}
|
|
|
|
else if (depth == 16)
|
|
|
|
{
|
|
|
|
format = GDK_MEMORY_G16A16;
|
|
|
|
}
|
|
|
|
break;
|
2021-09-10 16:44:43 +00:00
|
|
|
default:
|
2021-09-26 04:44:56 +00:00
|
|
|
png_destroy_read_struct (&png, &info, NULL);
|
|
|
|
g_set_error (error,
|
|
|
|
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
|
2021-11-05 18:42:20 +00:00
|
|
|
_("Unsupported color type %u in png image"), color_type);
|
2021-09-26 04:44:56 +00:00
|
|
|
return NULL;
|
2021-09-10 16:44:43 +00:00
|
|
|
}
|
|
|
|
|
2024-07-16 15:48:50 +00:00
|
|
|
color_state = gdk_png_get_color_state (png, info, error);
|
|
|
|
if (color_state == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2021-09-15 04:41:40 +00:00
|
|
|
bpp = gdk_memory_format_bytes_per_pixel (format);
|
2024-01-01 23:54:36 +00:00
|
|
|
if (!g_size_checked_mul (&stride, width, bpp) ||
|
|
|
|
!g_size_checked_add (&stride, stride, (8 - stride % 8) % 8))
|
|
|
|
{
|
|
|
|
g_set_error (error,
|
|
|
|
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_TOO_LARGE,
|
|
|
|
_("Image stride too large for image size %ux%u"), width, height);
|
|
|
|
return NULL;
|
|
|
|
}
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
buffer = g_try_malloc_n (height, stride);
|
|
|
|
row_pointers = g_try_malloc_n (height, sizeof (char *));
|
|
|
|
|
|
|
|
if (!buffer || !row_pointers)
|
|
|
|
{
|
2024-07-16 15:48:50 +00:00
|
|
|
gdk_color_state_unref (color_state);
|
2021-09-10 16:44:43 +00:00
|
|
|
g_free (buffer);
|
|
|
|
g_free (row_pointers);
|
|
|
|
png_destroy_read_struct (&png, &info, NULL);
|
2021-09-17 01:25:35 +00:00
|
|
|
g_set_error (error,
|
|
|
|
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_TOO_LARGE,
|
|
|
|
_("Not enough memory for image size %ux%u"), width, height);
|
2021-09-10 16:44:43 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-01-01 23:54:36 +00:00
|
|
|
for (i = 0; i < height; i++)
|
2021-09-10 16:44:43 +00:00
|
|
|
row_pointers[i] = &buffer[i * stride];
|
|
|
|
|
|
|
|
png_read_image (png, row_pointers);
|
|
|
|
png_read_end (png, info);
|
|
|
|
|
|
|
|
out_bytes = g_bytes_new_take (buffer, height * stride);
|
2022-05-08 14:12:59 +00:00
|
|
|
builder = gdk_memory_texture_builder_new ();
|
|
|
|
gdk_memory_texture_builder_set_format (builder, format);
|
2024-07-16 15:48:50 +00:00
|
|
|
gdk_memory_texture_builder_set_color_state (builder, color_state);
|
2022-05-08 14:12:59 +00:00
|
|
|
gdk_memory_texture_builder_set_width (builder, width);
|
|
|
|
gdk_memory_texture_builder_set_height (builder, height);
|
|
|
|
gdk_memory_texture_builder_set_bytes (builder, out_bytes);
|
|
|
|
gdk_memory_texture_builder_set_stride (builder, stride);
|
|
|
|
texture = gdk_memory_texture_builder_build (builder);
|
|
|
|
g_object_unref (builder);
|
2021-09-10 16:44:43 +00:00
|
|
|
g_bytes_unref (out_bytes);
|
2024-07-16 15:48:50 +00:00
|
|
|
gdk_color_state_unref (color_state);
|
2021-09-10 16:44:43 +00:00
|
|
|
|
2024-04-28 19:07:25 +00:00
|
|
|
if (options && png_get_text (png, info, &text, &num_texts))
|
|
|
|
{
|
|
|
|
for (i = 0; i < num_texts; i++)
|
|
|
|
{
|
|
|
|
if (text->compression != -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
g_hash_table_insert (options, g_strdup (text->key), g_strdup (text->text));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
g_free (row_pointers);
|
|
|
|
png_destroy_read_struct (&png, &info, NULL);
|
|
|
|
|
2021-09-18 18:12:39 +00:00
|
|
|
if (GDK_PROFILER_IS_RUNNING)
|
|
|
|
{
|
|
|
|
gint64 end = GDK_PROFILER_CURRENT_TIME;
|
|
|
|
if (end - before > 500000)
|
2024-01-21 18:58:09 +00:00
|
|
|
gdk_profiler_add_mark (before, end - before, "Load png", NULL);
|
2021-09-18 18:12:39 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
|
|
|
GBytes *
|
|
|
|
gdk_save_png (GdkTexture *texture)
|
|
|
|
{
|
|
|
|
png_struct *png = NULL;
|
|
|
|
png_info *info;
|
|
|
|
png_io io = { NULL, 0, 0 };
|
2021-09-27 03:55:48 +00:00
|
|
|
int width, height;
|
2021-09-10 16:44:43 +00:00
|
|
|
int y;
|
2021-09-15 04:41:40 +00:00
|
|
|
GdkMemoryFormat format;
|
2023-02-14 06:19:18 +00:00
|
|
|
GdkTextureDownloader downloader;
|
|
|
|
GBytes *bytes;
|
|
|
|
gsize stride;
|
|
|
|
const guchar *data;
|
2024-07-16 15:48:50 +00:00
|
|
|
GdkColorState *color_state;
|
2021-09-15 04:41:40 +00:00
|
|
|
int png_format;
|
|
|
|
int depth;
|
2024-07-16 15:48:50 +00:00
|
|
|
png_byte chunk_data[4];
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
width = gdk_texture_get_width (texture);
|
|
|
|
height = gdk_texture_get_height (texture);
|
2024-07-16 15:48:50 +00:00
|
|
|
color_state = gdk_texture_get_color_state (texture);
|
2021-10-07 04:19:41 +00:00
|
|
|
format = gdk_texture_get_format (texture);
|
2021-09-10 16:44:43 +00:00
|
|
|
|
2021-09-15 04:41:40 +00:00
|
|
|
switch (format)
|
|
|
|
{
|
|
|
|
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
|
|
|
|
case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
|
|
|
|
case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
|
2023-10-20 03:20:15 +00:00
|
|
|
case GDK_MEMORY_A8B8G8R8_PREMULTIPLIED:
|
2021-09-15 04:41:40 +00:00
|
|
|
case GDK_MEMORY_B8G8R8A8:
|
|
|
|
case GDK_MEMORY_A8R8G8B8:
|
|
|
|
case GDK_MEMORY_R8G8B8A8:
|
|
|
|
case GDK_MEMORY_A8B8G8R8:
|
2021-09-27 03:55:48 +00:00
|
|
|
format = GDK_MEMORY_R8G8B8A8;
|
|
|
|
png_format = PNG_COLOR_TYPE_RGB_ALPHA;
|
|
|
|
depth = 8;
|
|
|
|
break;
|
|
|
|
|
2021-09-15 04:41:40 +00:00
|
|
|
case GDK_MEMORY_R8G8B8:
|
|
|
|
case GDK_MEMORY_B8G8R8:
|
2023-10-20 11:33:46 +00:00
|
|
|
case GDK_MEMORY_R8G8B8X8:
|
|
|
|
case GDK_MEMORY_X8R8G8B8:
|
|
|
|
case GDK_MEMORY_B8G8R8X8:
|
|
|
|
case GDK_MEMORY_X8B8G8R8:
|
2021-09-27 03:55:48 +00:00
|
|
|
format = GDK_MEMORY_R8G8B8;
|
|
|
|
png_format = PNG_COLOR_TYPE_RGB;
|
2021-09-15 04:41:40 +00:00
|
|
|
depth = 8;
|
|
|
|
break;
|
|
|
|
|
2021-09-26 14:47:34 +00:00
|
|
|
case GDK_MEMORY_R16G16B16A16:
|
2021-09-15 04:41:40 +00:00
|
|
|
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
|
2021-09-26 14:47:34 +00:00
|
|
|
case GDK_MEMORY_R16G16B16A16_FLOAT:
|
2021-09-15 04:41:40 +00:00
|
|
|
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
|
2021-09-26 14:47:34 +00:00
|
|
|
case GDK_MEMORY_R32G32B32A32_FLOAT:
|
2021-09-15 04:41:40 +00:00
|
|
|
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
|
2021-09-27 03:55:48 +00:00
|
|
|
format = GDK_MEMORY_R16G16B16A16;
|
2021-09-15 04:41:40 +00:00
|
|
|
png_format = PNG_COLOR_TYPE_RGB_ALPHA;
|
2021-09-27 03:55:48 +00:00
|
|
|
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;
|
2021-09-15 04:41:40 +00:00
|
|
|
depth = 16;
|
|
|
|
break;
|
|
|
|
|
2023-05-26 13:35:57 +00:00
|
|
|
case GDK_MEMORY_G8:
|
|
|
|
format = GDK_MEMORY_G8;
|
|
|
|
png_format = PNG_COLOR_TYPE_GRAY;
|
|
|
|
depth = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GDK_MEMORY_G8A8_PREMULTIPLIED:
|
|
|
|
case GDK_MEMORY_G8A8:
|
|
|
|
case GDK_MEMORY_A8:
|
|
|
|
format = GDK_MEMORY_G8A8;
|
|
|
|
png_format = PNG_COLOR_TYPE_GRAY_ALPHA;
|
|
|
|
depth = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GDK_MEMORY_G16:
|
|
|
|
format = GDK_MEMORY_G16;
|
|
|
|
png_format = PNG_COLOR_TYPE_GRAY;
|
|
|
|
depth = 16;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GDK_MEMORY_G16A16_PREMULTIPLIED:
|
|
|
|
case GDK_MEMORY_G16A16:
|
|
|
|
case GDK_MEMORY_A16:
|
2023-07-21 01:02:33 +00:00
|
|
|
case GDK_MEMORY_A16_FLOAT:
|
|
|
|
case GDK_MEMORY_A32_FLOAT:
|
2023-05-26 13:35:57 +00:00
|
|
|
format = GDK_MEMORY_G16A16;
|
|
|
|
png_format = PNG_COLOR_TYPE_GRAY_ALPHA;
|
|
|
|
depth = 16;
|
|
|
|
break;
|
|
|
|
|
2021-09-15 04:41:40 +00:00
|
|
|
case GDK_MEMORY_N_FORMATS:
|
|
|
|
default:
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
png = png_create_write_struct_2 (PNG_LIBPNG_VER_STRING, NULL,
|
|
|
|
png_simple_error_callback,
|
|
|
|
png_simple_warning_callback,
|
|
|
|
NULL,
|
|
|
|
png_malloc_callback,
|
|
|
|
png_free_callback);
|
|
|
|
if (!png)
|
|
|
|
return NULL;
|
|
|
|
|
2023-12-30 19:14:10 +00:00
|
|
|
/* 2^31-1 is the maximum size for PNG files */
|
|
|
|
png_set_user_limits (png, (1u << 31) - 1, (1u << 31) - 1);
|
|
|
|
|
2024-07-16 15:48:50 +00:00
|
|
|
png_set_keep_unknown_chunks (png, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0);
|
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
info = png_create_info_struct (png);
|
|
|
|
if (!info)
|
|
|
|
{
|
|
|
|
png_destroy_read_struct (&png, NULL, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-07-16 15:48:50 +00:00
|
|
|
gdk_color_state_ref (color_state);
|
2024-07-16 19:14:13 +00:00
|
|
|
bytes = NULL;
|
2021-10-22 21:51:26 +00:00
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
if (sigsetjmp (png_jmpbuf (png), 1))
|
|
|
|
{
|
2024-07-16 15:48:50 +00:00
|
|
|
gdk_color_state_unref (color_state);
|
2024-07-16 19:14:13 +00:00
|
|
|
g_clear_pointer (&bytes, g_bytes_unref);
|
2021-09-15 04:41:40 +00:00
|
|
|
g_free (io.data);
|
2021-09-10 16:44:43 +00:00
|
|
|
png_destroy_read_struct (&png, &info, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
png_set_write_fn (png, &io, png_write_func, png_flush_func);
|
|
|
|
|
2021-09-15 04:41:40 +00:00
|
|
|
png_set_IHDR (png, info, width, height, depth,
|
|
|
|
png_format,
|
2021-09-10 16:44:43 +00:00
|
|
|
PNG_INTERLACE_NONE,
|
2021-09-15 04:41:40 +00:00
|
|
|
PNG_COMPRESSION_TYPE_DEFAULT,
|
|
|
|
PNG_FILTER_TYPE_DEFAULT);
|
2021-09-10 16:44:43 +00:00
|
|
|
|
2024-07-16 15:48:50 +00:00
|
|
|
gdk_png_set_color_state (png, info, color_state, chunk_data);
|
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
png_write_info (png, info);
|
2021-09-15 04:41:40 +00:00
|
|
|
|
|
|
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
|
|
png_set_swap (png);
|
|
|
|
#endif
|
2021-09-10 16:44:43 +00:00
|
|
|
|
2024-07-16 19:14:13 +00:00
|
|
|
gdk_texture_downloader_init (&downloader, texture);
|
|
|
|
gdk_texture_downloader_set_format (&downloader, format);
|
|
|
|
bytes = gdk_texture_downloader_download_bytes (&downloader, &stride);
|
|
|
|
gdk_texture_downloader_finish (&downloader);
|
|
|
|
data = g_bytes_get_data (bytes, NULL);
|
|
|
|
|
2021-09-27 03:55:48 +00:00
|
|
|
for (y = 0; y < height; y++)
|
|
|
|
png_write_row (png, data + y * stride);
|
2021-09-10 16:44:43 +00:00
|
|
|
|
|
|
|
png_write_end (png, info);
|
|
|
|
|
|
|
|
png_destroy_write_struct (&png, &info);
|
|
|
|
|
2024-07-16 15:48:50 +00:00
|
|
|
gdk_color_state_unref (color_state);
|
2023-02-14 06:19:18 +00:00
|
|
|
g_bytes_unref (bytes);
|
2021-09-15 04:41:40 +00:00
|
|
|
|
2021-09-10 16:44:43 +00:00
|
|
|
return g_bytes_new_take (io.data, io.size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* }}} */
|
|
|
|
|
|
|
|
/* vim:set foldmethod=marker expandtab: */
|