Compare commits
20 Commits
path-point
...
gamma-shen
Author | SHA1 | Date | |
---|---|---|---|
|
9d4f2414ba | ||
|
1c8c852c1b | ||
|
5923766623 | ||
|
da59d7080e | ||
|
53f6766bb3 | ||
|
7725c8439c | ||
|
5a9c81ab6f | ||
|
32ef5bed92 | ||
|
5e42e78f2d | ||
|
751ccf460d | ||
|
0f43857676 | ||
|
d389c113e9 | ||
|
97e34ed859 | ||
|
512580ba0d | ||
|
dcde46ab30 | ||
|
9a9d20e274 | ||
|
4fcb46a387 | ||
|
3e4f8dc356 | ||
|
e1295d3db1 | ||
|
f64304503c |
@@ -25,6 +25,8 @@
|
||||
#include "filetransferportalprivate.h"
|
||||
#include "gdktexture.h"
|
||||
#include "gdkrgbaprivate.h"
|
||||
#include "gdkpng.h"
|
||||
#include "gdktiff.h"
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
@@ -655,6 +657,66 @@ pixbuf_deserializer (GdkContentDeserializer *deserializer)
|
||||
deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
png_deserializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer deserializer)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GValue *value;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_png_finish (res, &error);
|
||||
if (texture == NULL)
|
||||
{
|
||||
gdk_content_deserializer_return_error (deserializer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
value = gdk_content_deserializer_get_value (deserializer);
|
||||
g_value_take_object (value, texture);
|
||||
gdk_content_deserializer_return_success (deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
png_deserializer (GdkContentDeserializer *deserializer)
|
||||
{
|
||||
gdk_load_png_async (gdk_content_deserializer_get_input_stream (deserializer),
|
||||
gdk_content_deserializer_get_cancellable (deserializer),
|
||||
png_deserializer_finish,
|
||||
deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
tiff_deserializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer deserializer)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GValue *value;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_tiff_finish (res, &error);
|
||||
if (texture == NULL)
|
||||
{
|
||||
gdk_content_deserializer_return_error (deserializer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
value = gdk_content_deserializer_get_value (deserializer);
|
||||
g_value_take_object (value, texture);
|
||||
gdk_content_deserializer_return_success (deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
tiff_deserializer (GdkContentDeserializer *deserializer)
|
||||
{
|
||||
gdk_load_tiff_async (gdk_content_deserializer_get_input_stream (deserializer),
|
||||
gdk_content_deserializer_get_cancellable (deserializer),
|
||||
tiff_deserializer_finish,
|
||||
deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
string_deserializer_finish (GObject *source,
|
||||
GAsyncResult *result,
|
||||
@@ -863,27 +925,38 @@ init (void)
|
||||
|
||||
initialized = TRUE;
|
||||
|
||||
gdk_content_register_deserializer ("image/png",
|
||||
GDK_TYPE_TEXTURE,
|
||||
png_deserializer,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
gdk_content_register_deserializer ("image/tiff",
|
||||
GDK_TYPE_TEXTURE,
|
||||
tiff_deserializer,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
formats = gdk_pixbuf_get_formats ();
|
||||
|
||||
/* Make sure png comes first */
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
char *name;
|
||||
|
||||
char *name;
|
||||
|
||||
name = gdk_pixbuf_format_get_name (fmt);
|
||||
if (g_str_equal (name, "png"))
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
|
||||
g_free (name);
|
||||
|
||||
break;
|
||||
}
|
||||
g_free (name);
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
|
@@ -26,6 +26,9 @@
|
||||
#include "filetransferportalprivate.h"
|
||||
#include "gdktextureprivate.h"
|
||||
#include "gdkrgba.h"
|
||||
#include "gdkpng.h"
|
||||
#include "gdktiff.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <string.h>
|
||||
@@ -606,6 +609,7 @@ gdk_content_serialize_finish (GAsyncResult *result,
|
||||
|
||||
/*** SERIALIZERS ***/
|
||||
|
||||
|
||||
static void
|
||||
pixbuf_serializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
@@ -658,6 +662,132 @@ pixbuf_serializer (GdkContentSerializer *serializer)
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GdkContentSerializer *serializer;
|
||||
GBytes *bytes;
|
||||
} PngSerializerData;
|
||||
|
||||
static void
|
||||
png_serializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PngSerializerData *data = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gdk_save_png_finish (res, &error))
|
||||
gdk_content_serializer_return_error (data->serializer, error);
|
||||
else
|
||||
gdk_content_serializer_return_success (data->serializer);
|
||||
|
||||
g_bytes_unref (data->bytes);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
png_serializer (GdkContentSerializer *serializer)
|
||||
{
|
||||
const GValue *value;
|
||||
GdkTexture *texture;
|
||||
GdkMemoryFormat format;
|
||||
GBytes *bytes = NULL;
|
||||
PngSerializerData *data;
|
||||
|
||||
value = gdk_content_serializer_get_value (serializer);
|
||||
|
||||
texture = g_value_get_object (value);
|
||||
|
||||
for (int i = 0; i < GDK_MEMORY_N_FORMATS; i++)
|
||||
{
|
||||
bytes = gdk_texture_download_format (texture, i);
|
||||
if (bytes)
|
||||
{
|
||||
format = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (bytes != NULL);
|
||||
|
||||
data = g_new0 (PngSerializerData, 1);
|
||||
data->serializer = serializer;
|
||||
data->bytes = bytes;
|
||||
|
||||
gdk_save_png_async (gdk_content_serializer_get_output_stream (serializer),
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture),
|
||||
gdk_texture_get_width (texture) * gdk_memory_format_bytes_per_pixel (format),
|
||||
format,
|
||||
gdk_content_serializer_get_cancellable (serializer),
|
||||
png_serializer_finish,
|
||||
data);
|
||||
g_object_unref (texture);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GdkContentSerializer *serializer;
|
||||
GBytes *bytes;
|
||||
} TiffSerializerData;
|
||||
|
||||
static void
|
||||
tiff_serializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
TiffSerializerData *data = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gdk_save_tiff_finish (res, &error))
|
||||
gdk_content_serializer_return_error (data->serializer, error);
|
||||
else
|
||||
gdk_content_serializer_return_success (data->serializer);
|
||||
|
||||
g_bytes_unref (data->bytes);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
tiff_serializer (GdkContentSerializer *serializer)
|
||||
{
|
||||
const GValue *value;
|
||||
GdkTexture *texture;
|
||||
GdkMemoryFormat format;
|
||||
GBytes *bytes = NULL;
|
||||
TiffSerializerData *data;
|
||||
|
||||
value = gdk_content_serializer_get_value (serializer);
|
||||
|
||||
texture = g_value_get_object (value);
|
||||
|
||||
for (int i = 0; i < GDK_MEMORY_N_FORMATS; i++)
|
||||
{
|
||||
bytes = gdk_texture_download_format (texture, i);
|
||||
if (bytes)
|
||||
{
|
||||
format = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (bytes != NULL);
|
||||
|
||||
data = g_new0 (TiffSerializerData, 1);
|
||||
data->serializer = serializer;
|
||||
data->bytes = bytes;
|
||||
|
||||
gdk_save_tiff_async (gdk_content_serializer_get_output_stream (serializer),
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture),
|
||||
gdk_texture_get_width (texture) * gdk_memory_format_bytes_per_pixel (format),
|
||||
format,
|
||||
gdk_content_serializer_get_cancellable (serializer),
|
||||
tiff_serializer_finish,
|
||||
data);
|
||||
g_object_unref (texture);
|
||||
}
|
||||
|
||||
static void
|
||||
string_serializer_finish (GObject *source,
|
||||
GAsyncResult *result,
|
||||
@@ -877,27 +1007,36 @@ init (void)
|
||||
|
||||
initialized = TRUE;
|
||||
|
||||
gdk_content_register_serializer (GDK_TYPE_TEXTURE,
|
||||
"image/png",
|
||||
png_serializer,
|
||||
NULL, NULL);
|
||||
|
||||
gdk_content_register_serializer (GDK_TYPE_TEXTURE,
|
||||
"image/tiff",
|
||||
tiff_serializer,
|
||||
NULL, NULL);
|
||||
|
||||
formats = gdk_pixbuf_get_formats ();
|
||||
|
||||
/* Make sure png comes first */
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
char *name;
|
||||
|
||||
char *name;
|
||||
|
||||
name = gdk_pixbuf_format_get_name (fmt);
|
||||
if (g_str_equal (name, "png"))
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
|
||||
g_free (name);
|
||||
|
||||
break;
|
||||
}
|
||||
g_free (name);
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
|
@@ -278,6 +278,48 @@ gdk_gl_context_upload_texture (GdkGLContext *context,
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
bpp = 3;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R16G16B16)
|
||||
{
|
||||
gl_internalformat = GL_RGBA16;
|
||||
gl_format = GL_RGB;
|
||||
gl_type = GL_UNSIGNED_SHORT;
|
||||
bpp = 6;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED)
|
||||
{
|
||||
gl_internalformat = GL_RGBA16;
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_SHORT;
|
||||
bpp = 8;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R16G16B16_FLOAT)
|
||||
{
|
||||
gl_internalformat = GL_RGB16F;
|
||||
gl_format = GL_RGB;
|
||||
gl_type = GL_HALF_FLOAT;
|
||||
bpp = 6;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED)
|
||||
{
|
||||
gl_internalformat = GL_RGBA16F;
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_HALF_FLOAT;
|
||||
bpp = 8;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R32G32B32_FLOAT)
|
||||
{
|
||||
gl_internalformat = GL_RGB32F;
|
||||
gl_format = GL_RGB;
|
||||
gl_type = GL_FLOAT;
|
||||
bpp = 12;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED)
|
||||
{
|
||||
gl_internalformat = GL_RGBA32F;
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_FLOAT;
|
||||
bpp = 16;
|
||||
}
|
||||
else /* Fall-back, convert to cairo-surface-format */
|
||||
{
|
||||
copy = g_malloc (width * height * 4);
|
||||
|
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "gdkcairo.h"
|
||||
#include "gdktextureprivate.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gdkinternals.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
@@ -110,6 +112,78 @@ gdk_gl_texture_download (GdkTexture *texture,
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
|
||||
static int
|
||||
type_from_internal_format (int internal_format)
|
||||
{
|
||||
switch (internal_format)
|
||||
{
|
||||
case GL_RGB16:
|
||||
case GL_RGBA16:
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case GL_RGB16F:
|
||||
case GL_RGBA16F:
|
||||
return GL_HALF_FLOAT;
|
||||
case GL_RGB32F:
|
||||
case GL_RGBA32F:
|
||||
return GL_FLOAT;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
internal_format_for_format (GdkMemoryFormat format)
|
||||
{
|
||||
switch ((int)format)
|
||||
{
|
||||
case GDK_MEMORY_R16G16B16:
|
||||
return GL_RGB16;
|
||||
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
|
||||
return GL_RGBA16;
|
||||
case GDK_MEMORY_R16G16B16_FLOAT:
|
||||
return GL_RGB16F;
|
||||
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
|
||||
return GL_RGB16F;
|
||||
case GDK_MEMORY_R32G32B32_FLOAT:
|
||||
return GL_RGB32F;
|
||||
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
|
||||
return GL_RGB32F;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
gdk_gl_texture_download_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
GdkGLTexture *self = GDK_GL_TEXTURE (texture);
|
||||
GdkSurface *surface;
|
||||
GdkGLContext *context;
|
||||
int internal_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, &internal_format);
|
||||
|
||||
if (internal_format != internal_format_for_format (format))
|
||||
return NULL;
|
||||
|
||||
size = texture->width * texture->height * gdk_memory_format_bytes_per_pixel (format);
|
||||
data = malloc (size);
|
||||
|
||||
glReadPixels (0, 0, texture->width, texture->height,
|
||||
internal_format, type_from_internal_format (internal_format),
|
||||
data);
|
||||
|
||||
return g_bytes_new_take (data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_gl_texture_class_init (GdkGLTextureClass *klass)
|
||||
{
|
||||
@@ -117,6 +191,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_format = gdk_gl_texture_download_format;
|
||||
gobject_class->dispose = gdk_gl_texture_dispose;
|
||||
}
|
||||
|
||||
|
234
gdk/gdkjpeg.c
Normal file
234
gdk/gdkjpeg.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/* 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 "gdkjpeg.h"
|
||||
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
/* {{{ IO handling */
|
||||
|
||||
#define BUF_SIZE 65536
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_source_mgr pub;
|
||||
JOCTET buffer[BUF_SIZE];
|
||||
long skip_next;
|
||||
GInputStream *stream;
|
||||
} my_source_mgr;
|
||||
|
||||
static void
|
||||
init_source (j_decompress_ptr info)
|
||||
{
|
||||
my_source_mgr *src = (my_source_mgr *) info->src;
|
||||
src->skip_next = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
term_source (j_decompress_ptr info)
|
||||
{
|
||||
}
|
||||
|
||||
static boolean
|
||||
fill_input_buffer (j_decompress_ptr info)
|
||||
{
|
||||
my_source_mgr *src = (my_source_mgr *) info->src;
|
||||
size_t nbytes;
|
||||
|
||||
nbytes = g_input_stream_read (src->stream, src->buffer, BUF_SIZE, NULL, NULL);
|
||||
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
/* Insert a fake EOI marker */
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
nbytes = 2;
|
||||
}
|
||||
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = nbytes;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
skip_input_data (j_decompress_ptr info,
|
||||
long num_bytes)
|
||||
{
|
||||
my_source_mgr *src = (my_source_mgr *) info->src;
|
||||
|
||||
if (num_bytes > 0)
|
||||
{
|
||||
while (num_bytes > src->pub.bytes_in_buffer)
|
||||
{
|
||||
num_bytes -= src->pub.bytes_in_buffer;
|
||||
fill_input_buffer (info);
|
||||
}
|
||||
|
||||
src->pub.next_input_byte += num_bytes;
|
||||
src->pub.bytes_in_buffer -= num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Error handling */
|
||||
|
||||
struct error_handler_data {
|
||||
struct jpeg_error_mgr pub;
|
||||
sigjmp_buf setjmp_buffer;
|
||||
GError **error;
|
||||
};
|
||||
|
||||
static void
|
||||
fatal_error_handler (j_common_ptr cinfo)
|
||||
{
|
||||
struct error_handler_data *errmgr;
|
||||
|
||||
errmgr = (struct error_handler_data *) cinfo->err;
|
||||
|
||||
siglongjmp (errmgr->setjmp_buffer, 1);
|
||||
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
static void
|
||||
output_message_handler (j_common_ptr cinfo)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Public API */
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_jpeg (GInputStream *stream,
|
||||
GError **error)
|
||||
{
|
||||
struct jpeg_decompress_struct info;
|
||||
struct error_handler_data jerr;
|
||||
struct jpeg_error_mgr err;
|
||||
my_source_mgr src;
|
||||
int width, height;
|
||||
int size;
|
||||
unsigned char *data;
|
||||
unsigned char *row[1];
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
|
||||
info.err = jpeg_std_error (&jerr.pub);
|
||||
jerr.pub.error_exit = fatal_error_handler;
|
||||
jerr.pub.output_message = output_message_handler;
|
||||
jerr.error = error;
|
||||
|
||||
if (sigsetjmp (jerr.setjmp_buffer, 1))
|
||||
{
|
||||
jpeg_destroy_decompress (&info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info.err = jpeg_std_error (&err);
|
||||
jpeg_create_decompress (&info);
|
||||
|
||||
src.pub.init_source = init_source;
|
||||
src.pub.fill_input_buffer = fill_input_buffer;
|
||||
src.pub.skip_input_data = skip_input_data;
|
||||
src.pub.resync_to_restart = jpeg_resync_to_restart;
|
||||
src.pub.term_source = term_source;
|
||||
src.pub.bytes_in_buffer = 0;
|
||||
src.pub.next_input_byte = NULL;
|
||||
src.stream = stream;
|
||||
|
||||
info.src = (struct jpeg_source_mgr *)&src;
|
||||
|
||||
jpeg_read_header (&info, TRUE);
|
||||
jpeg_start_decompress (&info);
|
||||
|
||||
width = info.output_width;
|
||||
height = info.output_height;
|
||||
|
||||
size = width * height * 3;
|
||||
data = g_malloc (size);
|
||||
|
||||
while (info.output_scanline < info.output_height)
|
||||
{
|
||||
row[0] = (unsigned char *)(&data[3 *info.output_width * info.output_scanline]);
|
||||
jpeg_read_scanlines (&info, row, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_decompress (&info);
|
||||
jpeg_destroy_decompress (&info);
|
||||
|
||||
bytes = g_bytes_new_take (data, size);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
GDK_MEMORY_R8G8B8,
|
||||
bytes, width * 3);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Async code */
|
||||
|
||||
static void
|
||||
load_jpeg_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GInputStream *stream = source_object;
|
||||
GdkTexture *texture;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_jpeg (stream, &error);
|
||||
|
||||
if (texture)
|
||||
g_task_return_pointer (task, texture, g_object_unref);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_load_jpeg_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_run_in_thread (task, load_jpeg_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_jpeg_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
35
gdk/gdkjpeg.h
Normal file
35
gdk/gdkjpeg.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_JPEG_H__
|
||||
#define __GDK_JPEG_H__
|
||||
|
||||
#include "gdkmemorytexture.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
GdkTexture *gdk_load_jpeg (GInputStream *stream,
|
||||
GError **error);
|
||||
|
||||
void gdk_load_jpeg_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GdkTexture *gdk_load_jpeg_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
#endif
|
@@ -20,6 +20,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gsk/ngl/fp16private.h"
|
||||
|
||||
/**
|
||||
* GdkMemoryTexture:
|
||||
@@ -62,6 +63,22 @@ gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format)
|
||||
case GDK_MEMORY_B8G8R8:
|
||||
return 3;
|
||||
|
||||
case GDK_MEMORY_R16G16B16:
|
||||
return 6;
|
||||
|
||||
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
|
||||
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
|
||||
return 8;
|
||||
|
||||
case GDK_MEMORY_R16G16B16_FLOAT:
|
||||
return 6;
|
||||
|
||||
case GDK_MEMORY_R32G32B32_FLOAT:
|
||||
return 12;
|
||||
|
||||
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
|
||||
return 16;
|
||||
|
||||
case GDK_MEMORY_N_FORMATS:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -97,6 +114,18 @@ gdk_memory_texture_download (GdkTexture *texture,
|
||||
area->width, area->height);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
gdk_memory_texture_download_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
|
||||
|
||||
if (self->format == format)
|
||||
return g_bytes_ref (self->bytes);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
|
||||
{
|
||||
@@ -104,6 +133,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_format = gdk_memory_texture_download_format;
|
||||
gobject_class->dispose = gdk_memory_texture_dispose;
|
||||
}
|
||||
|
||||
@@ -148,7 +178,7 @@ gdk_memory_texture_new (int width,
|
||||
return GDK_TEXTURE (self);
|
||||
}
|
||||
|
||||
GdkMemoryFormat
|
||||
GdkMemoryFormat
|
||||
gdk_memory_texture_get_format (GdkMemoryTexture *self)
|
||||
{
|
||||
return self->format;
|
||||
@@ -169,8 +199,10 @@ gdk_memory_texture_get_stride (GdkMemoryTexture *self)
|
||||
static void
|
||||
convert_memcpy (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
@@ -184,8 +216,10 @@ convert_memcpy (guchar *dest_data,
|
||||
static void \
|
||||
convert_swizzle ## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
@@ -215,8 +249,10 @@ SWIZZLE(1,2,3,0)
|
||||
static void \
|
||||
convert_swizzle_opaque_## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
@@ -248,8 +284,10 @@ static void \
|
||||
convert_swizzle_premultiply_ ## A ## R ## G ## B ## _ ## A2 ## R2 ## G2 ## B2 \
|
||||
(guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
@@ -283,24 +321,447 @@ 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_OPAQUE(A,R,G,B) \
|
||||
static void \
|
||||
convert_16to8_swizzle_opaque_ ## A ## R ## G ## B (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; \
|
||||
\
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
guint16 *src = (guint16 *)src_data; \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = 255; \
|
||||
dest_data[4 * x + R] = (guchar)(src[4 * x + 0] >> 8); \
|
||||
dest_data[4 * x + G] = (guchar)(src[4 * x + 1] >> 8); \
|
||||
dest_data[4 * x + B] = (guchar)(src[4 * x + 2] >> 8); \
|
||||
} \
|
||||
\
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
}
|
||||
|
||||
SWIZZLE_16TO8_OPAQUE(0,1,2,3)
|
||||
SWIZZLE_16TO8_OPAQUE(2,1,0,3)
|
||||
SWIZZLE_16TO8_OPAQUE(1,2,3,0)
|
||||
|
||||
#define SWIZZLE_16TO8(A,R,G,B) \
|
||||
static void \
|
||||
convert_16to8_swizzle_ ## A ## R ## G ## B (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; \
|
||||
\
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
guint16 *src = (guint16 *)src_data; \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = (guchar)(src[4 * x + 0] >> 8); \
|
||||
dest_data[4 * x + R] = (guchar)(src[4 * x + 1] >> 8); \
|
||||
dest_data[4 * x + G] = (guchar)(src[4 * x + 2] >> 8); \
|
||||
dest_data[4 * x + B] = (guchar)(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)
|
||||
|
||||
#define SWIZZLE_FP16_OPAQUE(A,R,G,B) \
|
||||
static void \
|
||||
convert_fp16_swizzle_opaque_ ## A ## R ## G ## B (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; \
|
||||
float *c; \
|
||||
\
|
||||
c = g_malloc (width * sizeof (float)); \
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
guint16 *src = (guint16 *)src_data; \
|
||||
half_to_float (src, c, width); \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = 255; \
|
||||
dest_data[4 * x + R] = (guchar)(255 * c[3 * x + 0]); \
|
||||
dest_data[4 * x + G] = (guchar)(255 * c[3 * x + 1]); \
|
||||
dest_data[4 * x + B] = (guchar)(255 * c[3 * x + 2]); \
|
||||
} \
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
g_free (c); \
|
||||
}
|
||||
|
||||
SWIZZLE_FP16_OPAQUE(3,2,1,0)
|
||||
SWIZZLE_FP16_OPAQUE(0,1,2,3)
|
||||
SWIZZLE_FP16_OPAQUE(3,0,1,2)
|
||||
|
||||
#define SWIZZLE_FP16(A,R,G,B) \
|
||||
static void \
|
||||
convert_fp16_swizzle_ ## A ## R ## G ## B (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; \
|
||||
float *c; \
|
||||
\
|
||||
c = g_malloc (width * sizeof (float)); \
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
guint16 *src = (guint16 *)src_data; \
|
||||
half_to_float (src, c, width); \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = (guchar)(255 * c[4 * x + 0]); \
|
||||
dest_data[4 * x + R] = (guchar)(255 * c[4 * x + 1]); \
|
||||
dest_data[4 * x + G] = (guchar)(255 * c[4 * x + 2]); \
|
||||
dest_data[4 * x + B] = (guchar)(255 * c[4 * x + 3]); \
|
||||
} \
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
g_free (c); \
|
||||
}
|
||||
|
||||
SWIZZLE_FP16(3,2,1,0)
|
||||
SWIZZLE_FP16(0,1,2,3)
|
||||
SWIZZLE_FP16(3,0,1,2)
|
||||
|
||||
#define SWIZZLE_FLOAT_OPAQUE(A,R,G,B) \
|
||||
static void \
|
||||
convert_float_swizzle_opaque_ ## A ## R ## G ## B (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; \
|
||||
\
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
float *src = (float *)src_data; \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = 255; \
|
||||
dest_data[4 * x + R] = (guchar)(255 * src[3 * x + 0]); \
|
||||
dest_data[4 * x + G] = (guchar)(255 * src[3 * x + 1]); \
|
||||
dest_data[4 * x + B] = (guchar)(255 * src[3 * x + 2]); \
|
||||
} \
|
||||
\
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
}
|
||||
|
||||
SWIZZLE_FLOAT_OPAQUE(3,2,1,0)
|
||||
SWIZZLE_FLOAT_OPAQUE(0,1,2,3)
|
||||
SWIZZLE_FLOAT_OPAQUE(3,0,1,2)
|
||||
|
||||
#define SWIZZLE_FLOAT(A,R,G,B) \
|
||||
static void \
|
||||
convert_float_swizzle_ ## A ## R ## G ## B (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; \
|
||||
\
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
float *src = (float *)src_data; \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = (guchar)(255 * src[3 * x + 0]); \
|
||||
dest_data[4 * x + R] = (guchar)(255 * src[3 * x + 1]); \
|
||||
dest_data[4 * x + G] = (guchar)(255 * src[3 * x + 2]); \
|
||||
dest_data[4 * x + B] = (guchar)(255 * src[3 * x + 3]); \
|
||||
} \
|
||||
\
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
}
|
||||
|
||||
SWIZZLE_FLOAT(3,2,1,0)
|
||||
SWIZZLE_FLOAT(0,1,2,3)
|
||||
SWIZZLE_FLOAT(3,0,1,2)
|
||||
|
||||
typedef void (* ConversionFunc) (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height);
|
||||
|
||||
static ConversionFunc converters[GDK_MEMORY_N_FORMATS][3] =
|
||||
static ConversionFunc converters_to_bgra[GDK_MEMORY_N_FORMATS] =
|
||||
{
|
||||
{ convert_memcpy, convert_swizzle3210, convert_swizzle2103 },
|
||||
{ convert_swizzle3210, convert_memcpy, convert_swizzle3012 },
|
||||
{ convert_swizzle2103, convert_swizzle1230, convert_memcpy },
|
||||
{ convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210, convert_swizzle_premultiply_3012_3210, },
|
||||
{ convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123, convert_swizzle_premultiply_3012_0123 },
|
||||
{ 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_memcpy,
|
||||
convert_swizzle3210,
|
||||
convert_swizzle2103,
|
||||
convert_swizzle_premultiply_3210_3210,
|
||||
convert_swizzle_premultiply_3210_0123,
|
||||
convert_swizzle_premultiply_3210_3012,
|
||||
convert_swizzle_premultiply_3210_0321,
|
||||
convert_swizzle_opaque_3210,
|
||||
convert_swizzle_opaque_3012,
|
||||
convert_16to8_swizzle_opaque_2103,
|
||||
convert_16to8_swizzle_2103,
|
||||
convert_fp16_swizzle_opaque_3210,
|
||||
convert_fp16_swizzle_3210,
|
||||
convert_float_swizzle_opaque_3210,
|
||||
convert_float_swizzle_3210,
|
||||
};
|
||||
|
||||
static ConversionFunc converters_to_argb[GDK_MEMORY_N_FORMATS] =
|
||||
{
|
||||
convert_swizzle3210,
|
||||
convert_memcpy,
|
||||
convert_swizzle1230,
|
||||
convert_swizzle_premultiply_0123_3210,
|
||||
convert_swizzle_premultiply_0123_0123,
|
||||
convert_swizzle_premultiply_0123_3012,
|
||||
convert_swizzle_premultiply_0123_0321,
|
||||
convert_swizzle_opaque_0123,
|
||||
convert_swizzle_opaque_0321,
|
||||
convert_16to8_swizzle_opaque_1230,
|
||||
convert_16to8_swizzle_1230,
|
||||
convert_fp16_swizzle_opaque_0123,
|
||||
convert_fp16_swizzle_0123,
|
||||
convert_float_swizzle_opaque_0123,
|
||||
convert_float_swizzle_0123,
|
||||
};
|
||||
|
||||
static ConversionFunc converters_to_rgba[GDK_MEMORY_N_FORMATS] =
|
||||
{
|
||||
convert_swizzle2103,
|
||||
convert_swizzle3012,
|
||||
convert_memcpy,
|
||||
convert_swizzle_premultiply_3012_3210,
|
||||
convert_swizzle_premultiply_3012_0123,
|
||||
convert_swizzle_premultiply_3012_3012,
|
||||
convert_swizzle_premultiply_3012_0321,
|
||||
convert_swizzle_opaque_3012,
|
||||
convert_swizzle_opaque_3210,
|
||||
convert_16to8_swizzle_opaque_0123,
|
||||
convert_16to8_swizzle_0123,
|
||||
convert_fp16_swizzle_opaque_3012,
|
||||
convert_fp16_swizzle_3012,
|
||||
convert_float_swizzle_opaque_3012,
|
||||
convert_float_swizzle_3012
|
||||
};
|
||||
|
||||
static void
|
||||
convert_rgba_to_hdr (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;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
guint16 *src = (guint16 *)src_data;
|
||||
float *dest = (float *)dest_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
dest[4 * (y * width + x) + 0] = src[3 * (y * width + x) + 0] / 255.f;
|
||||
dest[4 * (y * width + x) + 1] = src[3 * (y * width + x) + 1] / 255.f;
|
||||
dest[4 * (y * width + x) + 2] = src[3 * (y * width + x) + 2] / 255.f;
|
||||
dest[4 * (y * width + x) + 3] = src[3 * (y * width + x) + 3] / 255.f;
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert_8bit_to_hdr (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
guchar *data;
|
||||
|
||||
/* TODO: this could perhaps be done in-place */
|
||||
data = g_malloc (width * height * 4);
|
||||
|
||||
gdk_memory_convert (data, 4 * width, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
src_data, src_stride, src_format,
|
||||
width, height);
|
||||
convert_rgba_to_hdr (dest_data, dest_stride, dest_format,
|
||||
data, 4 * width, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
width, height);
|
||||
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_16bit_to_hdr (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;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
guint16 *src = (guint16 *)src_data;
|
||||
float *dest = (float *)dest_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
dest[4 * (y * width + x) + 0] = src[3 * (y * width + x) + 0] / 65535.f;
|
||||
dest[4 * (y * width + x) + 1] = src[3 * (y * width + x) + 1] / 65535.f;
|
||||
dest[4 * (y * width + x) + 2] = src[3 * (y * width + x) + 2] / 65535.f;
|
||||
dest[4 * (y * width + x) + 3] = src[3 * (y * width + x) + 3] / 65535.f;
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert_fp16_to_hdr (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;
|
||||
int src_bpp;
|
||||
|
||||
if (src_format == GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED)
|
||||
src_bpp = 4;
|
||||
else
|
||||
src_bpp = 3;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
guint16 *src = (guint16 *)src_data;
|
||||
float *dest = (float *)dest_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
guint16 h[4];
|
||||
|
||||
h[0] = src[src_bpp * (y * width + x)];
|
||||
h[1] = src[src_bpp * (y * width + x) + 1];
|
||||
h[2] = src[src_bpp * (y * width + x) + 2];
|
||||
if (src_bpp == 4)
|
||||
h[3] = src[src_bpp * (y * width + x) + 3];
|
||||
else
|
||||
h[3] = FP16_ONE;
|
||||
half_to_float4 (h, &dest[4 * (y * width + x)]);
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert_float_to_hdr (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;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
float *src = (float *)src_data;
|
||||
float *dest = (float *)dest_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
dest[4 * (y * width + x) + 0] = src[3 * (y * width + x) + 0];
|
||||
dest[4 * (y * width + x) + 1] = src[3 * (y * width + x) + 1];
|
||||
dest[4 * (y * width + x) + 2] = src[3 * (y * width + x) + 2];
|
||||
dest[4 * (y * width + x) + 3] = 1.0;
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static ConversionFunc converters_to_hdr[GDK_MEMORY_N_FORMATS] =
|
||||
{
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_rgba_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_16bit_to_hdr,
|
||||
convert_fp16_to_hdr,
|
||||
convert_fp16_to_hdr,
|
||||
convert_float_to_hdr,
|
||||
convert_memcpy
|
||||
};
|
||||
static ConversionFunc* converters[GDK_MEMORY_N_FORMATS] = {
|
||||
converters_to_bgra, converters_to_argb, converters_to_rgba,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
converters_to_hdr
|
||||
};
|
||||
|
||||
void
|
||||
@@ -316,5 +777,7 @@ gdk_memory_convert (guchar *dest_data,
|
||||
g_assert (dest_format < 3);
|
||||
g_assert (src_format < GDK_MEMORY_N_FORMATS);
|
||||
|
||||
converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height);
|
||||
converters[dest_format][src_format] (dest_data, dest_stride, dest_format,
|
||||
src_data, src_stride, src_format,
|
||||
width, height);
|
||||
}
|
||||
|
@@ -42,6 +42,20 @@ 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_R16G16B16: 3 guint16 values; for red, green, blue. Since 4.6
|
||||
* @GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: 4 guint16 values; for red, green,
|
||||
* blue, alpha. The color values are premultiplied with the alpha value.
|
||||
* Since 4.6
|
||||
* @GDK_MEMORY_R16G16B16_FLOAT: 3 half-float values; for red, green, blue.
|
||||
* The data is opaque. Since 4.6
|
||||
* @GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: 4 half-float values; for
|
||||
* red, green, blue and alpha. The color values are premultiplied with
|
||||
* the alpha value. Since 4.6
|
||||
* @GDK_MEMORY_B32G32R32_FLOAT: 3 float values; for blue, green, red.
|
||||
* The data is opaque. Since 4.6
|
||||
* @GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: 4 float values; for
|
||||
* red, green, blue and alpha. The color values are premultiplied with
|
||||
* the alpha value. Since 4.6
|
||||
* @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 +67,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 +83,12 @@ typedef enum {
|
||||
GDK_MEMORY_A8B8G8R8,
|
||||
GDK_MEMORY_R8G8B8,
|
||||
GDK_MEMORY_B8G8R8,
|
||||
GDK_MEMORY_R16G16B16,
|
||||
GDK_MEMORY_R16G16B16A16_PREMULTIPLIED,
|
||||
GDK_MEMORY_R16G16B16_FLOAT,
|
||||
GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED,
|
||||
GDK_MEMORY_R32G32B32_FLOAT,
|
||||
GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED,
|
||||
|
||||
GDK_MEMORY_N_FORMATS
|
||||
} GdkMemoryFormat;
|
||||
|
489
gdk/gdkpng.c
Normal file
489
gdk/gdkpng.c
Normal file
@@ -0,0 +1,489 @@
|
||||
/* 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 "gdkpng.h"
|
||||
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gsk/ngl/fp16private.h"
|
||||
#include <png.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* {{{ IO handling */
|
||||
|
||||
#ifdef HAVE_FOPENCOOKIE
|
||||
|
||||
static ssize_t
|
||||
read_stream (void *cookie,
|
||||
char *buf,
|
||||
size_t size)
|
||||
{
|
||||
GInputStream *stream = cookie;
|
||||
|
||||
return g_input_stream_read (stream, buf, size, NULL, NULL);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
write_stream (void *cookie,
|
||||
const char *buf,
|
||||
size_t size)
|
||||
{
|
||||
GOutputStream *stream = cookie;
|
||||
|
||||
return g_output_stream_write (stream, buf, size, NULL, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
seek_stream (void *cookie,
|
||||
off64_t *offset,
|
||||
int whence)
|
||||
{
|
||||
GSeekable *seekable = cookie;
|
||||
GSeekType seek_type;
|
||||
|
||||
if (whence == SEEK_SET)
|
||||
seek_type = G_SEEK_SET;
|
||||
else if (whence == SEEK_CUR)
|
||||
seek_type = G_SEEK_CUR;
|
||||
else if (whence == SEEK_END)
|
||||
seek_type = G_SEEK_END;
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
if (g_seekable_seek (seekable, *offset, seek_type, NULL, NULL))
|
||||
{
|
||||
*offset = g_seekable_tell (seekable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
close_stream (void *cookie)
|
||||
{
|
||||
GObject *stream = cookie;
|
||||
|
||||
g_object_unref (stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cookie_io_functions_t cookie_funcs = {
|
||||
read_stream,
|
||||
write_stream,
|
||||
seek_stream,
|
||||
close_stream
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
static GBytes *
|
||||
read_all_data (GInputStream *source,
|
||||
GError **error)
|
||||
{
|
||||
GOutputStream *output;
|
||||
gssize size;
|
||||
GBytes *bytes;
|
||||
|
||||
output = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
|
||||
size = g_output_stream_splice (output, source, 0, NULL, error);
|
||||
if (size == -1)
|
||||
{
|
||||
g_object_unref (output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_output_stream_close (output, NULL, NULL);
|
||||
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output));
|
||||
g_object_unref (output);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#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 */
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_png (GInputStream *stream,
|
||||
GError **error)
|
||||
{
|
||||
png_image image = { NULL, PNG_IMAGE_VERSION, 0, };
|
||||
gsize size;
|
||||
gsize stride;
|
||||
guint16 *buffer;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
|
||||
#ifdef HAVE_FOPENCOOKIE
|
||||
FILE *file = fopencookie (g_object_ref (stream), "r", cookie_funcs);
|
||||
|
||||
png_image_begin_read_from_stdio (&image, file);
|
||||
#else
|
||||
GBytes *data;
|
||||
|
||||
data = read_all_data (stream, error);
|
||||
if (!data)
|
||||
return NULL;
|
||||
png_image_begin_read_from_memory (&image,
|
||||
g_bytes_get_data (data, NULL),
|
||||
g_bytes_get_size (data));
|
||||
#endif
|
||||
|
||||
image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
|
||||
|
||||
stride = PNG_IMAGE_ROW_STRIDE (image);
|
||||
size = PNG_IMAGE_BUFFER_SIZE (image, stride);
|
||||
buffer = g_malloc (size);
|
||||
|
||||
png_image_finish_read (&image, NULL, buffer, stride, NULL);
|
||||
|
||||
#ifdef HAVE_FOPENCOOKIE
|
||||
fclose (file);
|
||||
#else
|
||||
g_bytes_unref (data);
|
||||
#endif
|
||||
|
||||
bytes = g_bytes_new_take (buffer, size);
|
||||
|
||||
texture = gdk_memory_texture_new (image.width, image.height,
|
||||
GDK_MEMORY_R16G16B16A16_PREMULTIPLIED,
|
||||
bytes, 2 * stride);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
png_image_free (&image);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_save_png (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GError **error)
|
||||
{
|
||||
png_image image = { NULL, PNG_IMAGE_VERSION, 0, };
|
||||
gboolean result;
|
||||
guchar *new_data = NULL;
|
||||
|
||||
switch ((int)format)
|
||||
{
|
||||
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)
|
||||
stride /= 2;
|
||||
|
||||
#ifdef HAVE_FOPENCOOKIE
|
||||
FILE *file = fopencookie (g_object_ref (stream), "w", cookie_funcs);
|
||||
result = png_image_write_to_stdio (&image, file, FALSE, data, stride, NULL);
|
||||
fclose (file);
|
||||
#else
|
||||
gsize written;
|
||||
png_alloc_size_t size;
|
||||
gpointer buffer;
|
||||
png_image_write_get_memory_size (image, size, FALSE, data, stride, NULL);
|
||||
buffer = g_malloc (size);
|
||||
result = png_image_write_to_memory (&image, buffer, &size, FALSE, data, stride, NULL);
|
||||
if (result)
|
||||
result = g_output_stream_write_all (stream, buffer, (gsize)size, &written, NULL, NULL);
|
||||
g_free (buffer);
|
||||
#endif
|
||||
|
||||
if (!result)
|
||||
{
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Saving png failed");
|
||||
}
|
||||
|
||||
png_image_free (&image);
|
||||
|
||||
g_free (new_data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Async code */
|
||||
|
||||
static void
|
||||
load_png_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GInputStream *stream = source_object;
|
||||
GdkTexture *texture;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_png (stream, &error);
|
||||
|
||||
if (texture)
|
||||
g_task_return_pointer (task, texture, g_object_unref);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_load_png_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_run_in_thread (task, load_png_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_png_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const guchar *data;
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
GdkMemoryFormat format;
|
||||
} SavePngData;
|
||||
|
||||
static void
|
||||
save_png_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GOutputStream *stream = source_object;
|
||||
SavePngData *data = task_data;
|
||||
GError *error = NULL;
|
||||
gboolean result;
|
||||
|
||||
result = gdk_save_png (stream,
|
||||
data->data,
|
||||
data->width,
|
||||
data->height,
|
||||
data->stride,
|
||||
data->format,
|
||||
&error);
|
||||
|
||||
if (result)
|
||||
g_task_return_boolean (task, result);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_save_png_async (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
SavePngData *save_data;
|
||||
|
||||
save_data = g_new0 (SavePngData, 1);
|
||||
save_data->data = data;
|
||||
save_data->width = width;
|
||||
save_data->height = height;
|
||||
save_data->stride = stride;
|
||||
save_data->format = format;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_set_task_data (task, save_data, g_free);
|
||||
g_task_run_in_thread (task, save_png_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_save_png_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
56
gdk/gdkpng.h
Normal file
56
gdk/gdkpng.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_PNG_H__
|
||||
#define __GDK_PNG_H__
|
||||
|
||||
#include "gdkmemorytexture.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
GdkTexture *gdk_load_png (GInputStream *stream,
|
||||
GError **error);
|
||||
|
||||
void gdk_load_png_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GdkTexture *gdk_load_png_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
gboolean gdk_save_png (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GError **error);
|
||||
|
||||
void gdk_save_png_async (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean gdk_save_png_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
#endif
|
262
gdk/gdktexture.c
262
gdk/gdktexture.c
@@ -46,6 +46,9 @@
|
||||
#include "gdksnapshot.h"
|
||||
|
||||
#include <graphene.h>
|
||||
#include "gdkpng.h"
|
||||
#include "gdktiff.h"
|
||||
#include "gdkjpeg.h"
|
||||
|
||||
/* HACK: So we don't need to include any (not-yet-created) GSK or GTK headers */
|
||||
void
|
||||
@@ -124,6 +127,13 @@ gdk_texture_real_download (GdkTexture *self,
|
||||
GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
gdk_texture_real_download_format (GdkTexture *self,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_texture_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
@@ -188,6 +198,7 @@ gdk_texture_class_init (GdkTextureClass *klass)
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
klass->download = gdk_texture_real_download;
|
||||
klass->download_format = gdk_texture_real_download_format;
|
||||
|
||||
gobject_class->set_property = gdk_texture_set_property;
|
||||
gobject_class->get_property = gdk_texture_get_property;
|
||||
@@ -351,6 +362,10 @@ gdk_texture_new_from_resource (const char *resource_path)
|
||||
* The file format is detected automatically. The supported formats
|
||||
* are PNG and JPEG, though more formats might be available.
|
||||
*
|
||||
* For PNG files, this function supports conversion from SRGB to
|
||||
* linear data (including gamma correction) and can load 16bit
|
||||
* data.
|
||||
*
|
||||
* If %NULL is returned, then @error will be set.
|
||||
*
|
||||
* Return value: A newly-created `GdkTexture`
|
||||
@@ -362,6 +377,9 @@ gdk_texture_new_from_file (GFile *file,
|
||||
GdkTexture *texture;
|
||||
GdkPixbuf *pixbuf;
|
||||
GInputStream *stream;
|
||||
GInputStream *buffered;
|
||||
const void *data;
|
||||
gsize size;
|
||||
|
||||
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
@@ -370,6 +388,35 @@ gdk_texture_new_from_file (GFile *file,
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
buffered = g_buffered_input_stream_new (stream);
|
||||
g_object_unref (stream);
|
||||
stream = buffered;
|
||||
|
||||
g_buffered_input_stream_fill (G_BUFFERED_INPUT_STREAM (stream), 8, NULL, NULL);
|
||||
data = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (stream), &size);
|
||||
|
||||
if (memcmp (data, "\x89PNG", 4) == 0)
|
||||
{
|
||||
texture = gdk_load_png (stream, error);
|
||||
g_object_unref (stream);
|
||||
return texture;
|
||||
}
|
||||
|
||||
if (memcmp (data, "MM\x00\x2a", 4) == 0 ||
|
||||
memcmp (data, "II\x2a\x00", 4) == 0)
|
||||
{
|
||||
texture = gdk_load_tiff (stream, error);
|
||||
g_object_unref (stream);
|
||||
return texture;
|
||||
}
|
||||
|
||||
if (memcmp (data, "\xff\xd8", 2) == 0)
|
||||
{
|
||||
texture = gdk_load_jpeg (stream, error);
|
||||
g_object_unref (stream);
|
||||
return texture;
|
||||
}
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
|
||||
g_object_unref (stream);
|
||||
if (pixbuf == NULL)
|
||||
@@ -491,6 +538,95 @@ gdk_texture_download (GdkTexture *texture,
|
||||
stride);
|
||||
}
|
||||
|
||||
/* Returns the texture data in the requested format if that
|
||||
* can be done without conversion. NULL, otherwise.
|
||||
*/
|
||||
GBytes *
|
||||
gdk_texture_download_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
return GDK_TEXTURE_GET_CLASS (texture)->download_format (texture, format);
|
||||
}
|
||||
|
||||
/* Returns the texture data in the requested format, converting
|
||||
* it if necessary. This will only return NULL if we don't know
|
||||
* how to convert from the texture's format to the requested one.
|
||||
*/
|
||||
GBytes *
|
||||
gdk_texture_convert_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
GdkMemoryFormat src_format;
|
||||
GBytes *bytes;
|
||||
int width, height, stride;
|
||||
guchar *data;
|
||||
|
||||
for (int i = 0; i < GDK_MEMORY_N_FORMATS; i++)
|
||||
{
|
||||
bytes = gdk_texture_download_format (texture, i);
|
||||
if (bytes)
|
||||
{
|
||||
src_format = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bytes || src_format == format)
|
||||
return bytes;
|
||||
|
||||
/* convert from src_format to format */
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
stride = width * gdk_memory_format_bytes_per_pixel (format);
|
||||
data = g_malloc (height * stride);
|
||||
|
||||
gdk_memory_convert (data, stride, format,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes) / height,
|
||||
src_format,
|
||||
width, height);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return g_bytes_new_take (data, height * stride);
|
||||
}
|
||||
|
||||
/*
|
||||
* gdk_texture_download_float:
|
||||
* @texture: a `GdkTexture`
|
||||
* @data: (array): pointer to enough memory to be filled with the
|
||||
* downloaded data of @texture
|
||||
*
|
||||
* Downloads the @texture into local memory.
|
||||
*
|
||||
* This may be an expensive operation, as the actual texture data
|
||||
* may reside on a GPU or on a remote display server.
|
||||
*
|
||||
* The data format of the downloaded data is equivalent to
|
||||
* GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED, so every downloaded
|
||||
* pixel requires 16 bytes of memory.
|
||||
*
|
||||
* Note that the caller is responsible to provide sufficiently
|
||||
* aligned memory to access the resulting data directly as floats.
|
||||
*
|
||||
* Since: 4.6
|
||||
*/
|
||||
void
|
||||
gdk_texture_download_float (GdkTexture *texture,
|
||||
float *data)
|
||||
{
|
||||
GBytes *bytes;
|
||||
|
||||
bytes = gdk_texture_convert_format (texture, GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED);
|
||||
|
||||
g_assert (bytes);
|
||||
|
||||
memcpy (data, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_texture_set_render_data (GdkTexture *self,
|
||||
gpointer key,
|
||||
@@ -537,6 +673,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
|
||||
@@ -548,30 +687,121 @@ gboolean
|
||||
gdk_texture_save_to_png (GdkTexture *texture,
|
||||
const char *filename)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_status_t status;
|
||||
int width, height, stride;
|
||||
GBytes *bytes;
|
||||
GdkMemoryFormat format;
|
||||
gboolean result;
|
||||
GFile *file;
|
||||
GOutputStream *stream;
|
||||
|
||||
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);
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
|
||||
status = cairo_surface_write_to_png (surface, filename);
|
||||
|
||||
if (status != CAIRO_STATUS_SUCCESS ||
|
||||
cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
|
||||
result = FALSE;
|
||||
bytes = gdk_texture_download_format (texture, GDK_MEMORY_R16G16B16A16_PREMULTIPLIED);
|
||||
if (bytes)
|
||||
{
|
||||
format = GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
|
||||
stride = width * 8;
|
||||
}
|
||||
else
|
||||
result = TRUE;
|
||||
{
|
||||
gpointer data;
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
stride = width * 4;
|
||||
data = g_malloc (stride * height);
|
||||
|
||||
gdk_texture_download (texture, data, stride);
|
||||
|
||||
bytes = g_bytes_new_take (data, stride * height);
|
||||
|
||||
format = GDK_MEMORY_DEFAULT;
|
||||
}
|
||||
|
||||
file = g_file_new_for_path (filename);
|
||||
stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
|
||||
G_FILE_CREATE_NONE,
|
||||
NULL, NULL));
|
||||
g_object_unref (file);
|
||||
|
||||
result = gdk_save_png (stream,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
width, height, stride,
|
||||
format,
|
||||
NULL);
|
||||
|
||||
g_output_stream_close (stream, NULL, NULL);
|
||||
g_object_unref (stream);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_texture_save_to_file:
|
||||
* @texture: a `GdkTexture`
|
||||
* @filename: (type filename): the filename to store to
|
||||
*
|
||||
* Store the given @texture to the @filename.
|
||||
*
|
||||
* GTK will choose a suitable file format to save the data in
|
||||
* depending on the format of the texture. Currently, that is
|
||||
* tiff, for all kinds of textures.
|
||||
*
|
||||
* 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
|
||||
* look into using the gdk-pixbuf library.
|
||||
*
|
||||
* Returns: %TRUE if saving succeeded, %FALSE on failure.
|
||||
*
|
||||
* Since: 4.6
|
||||
*/
|
||||
gboolean
|
||||
gdk_texture_save_to_file (GdkTexture *texture,
|
||||
const char *filename)
|
||||
{
|
||||
GBytes *bytes = NULL;
|
||||
gboolean result;
|
||||
GdkMemoryFormat format;
|
||||
GFile *file;
|
||||
GOutputStream *stream;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
|
||||
for (int i = 0; i < GDK_MEMORY_N_FORMATS; i++)
|
||||
{
|
||||
bytes = gdk_texture_download_format (texture, i);
|
||||
if (bytes)
|
||||
{
|
||||
format = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bytes)
|
||||
return FALSE;
|
||||
|
||||
file = g_file_new_for_path (filename);
|
||||
stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
|
||||
G_FILE_CREATE_NONE,
|
||||
NULL, NULL));
|
||||
g_object_unref (file);
|
||||
|
||||
result = gdk_save_tiff (stream,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture),
|
||||
gdk_texture_get_width (texture) * gdk_memory_format_bytes_per_pixel (format),
|
||||
format,
|
||||
NULL);
|
||||
|
||||
g_clear_object (&stream);
|
||||
g_clear_pointer (&bytes, g_bytes_unref);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@@ -59,9 +59,15 @@ GDK_AVAILABLE_IN_ALL
|
||||
void gdk_texture_download (GdkTexture *texture,
|
||||
guchar *data,
|
||||
gsize stride);
|
||||
GDK_AVAILABLE_IN_4_6
|
||||
void gdk_texture_download_float (GdkTexture *texture,
|
||||
float *data);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gdk_texture_save_to_png (GdkTexture *texture,
|
||||
const char *filename);
|
||||
GDK_AVAILABLE_IN_4_6
|
||||
gboolean gdk_texture_save_to_file (GdkTexture *texture,
|
||||
const char *filename);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#define __GDK_TEXTURE_PRIVATE_H__
|
||||
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytexture.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -28,6 +29,8 @@ struct _GdkTextureClass {
|
||||
const GdkRectangle *area,
|
||||
guchar *data,
|
||||
gsize stride);
|
||||
GBytes * (* download_format) (GdkTexture *texture,
|
||||
GdkMemoryFormat format);
|
||||
};
|
||||
|
||||
gpointer gdk_texture_new (const GdkTextureClass *klass,
|
||||
@@ -48,6 +51,11 @@ void gdk_texture_clear_render_data (GdkTexture
|
||||
gpointer gdk_texture_get_render_data (GdkTexture *self,
|
||||
gpointer key);
|
||||
|
||||
GBytes * gdk_texture_download_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format);
|
||||
GBytes * gdk_texture_convert_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_TEXTURE_PRIVATE_H__ */
|
||||
|
628
gdk/gdktiff.c
Normal file
628
gdk/gdktiff.c
Normal file
@@ -0,0 +1,628 @@
|
||||
/* 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 "gdktiff.h"
|
||||
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include <tiffio.h>
|
||||
|
||||
/* {{{ IO handling */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GObject *stream;
|
||||
GInputStream *input;
|
||||
GOutputStream *output;
|
||||
|
||||
gchar *buffer;
|
||||
gsize allocated;
|
||||
gsize used;
|
||||
gsize position;
|
||||
} TiffIO;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
|
||||
|
||||
static void
|
||||
tiff_io_warning (const char *module,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap);
|
||||
}
|
||||
|
||||
static void
|
||||
tiff_io_error (const char *module,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
static tsize_t
|
||||
tiff_io_read (thandle_t handle,
|
||||
tdata_t buffer,
|
||||
tsize_t size)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
GError *error = NULL;
|
||||
gssize read = -1;
|
||||
gsize bytes_read = 0;
|
||||
|
||||
if (! g_input_stream_read_all (io->input,
|
||||
(void *) buffer, (gsize) size,
|
||||
&bytes_read,
|
||||
NULL, &error))
|
||||
{
|
||||
g_printerr ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
read = bytes_read;
|
||||
|
||||
return (tsize_t) read;
|
||||
}
|
||||
|
||||
static tsize_t
|
||||
tiff_io_write (thandle_t handle,
|
||||
tdata_t buffer,
|
||||
tsize_t size)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
GError *error = NULL;
|
||||
gssize written = -1;
|
||||
gsize bytes_written = 0;
|
||||
|
||||
if (! g_output_stream_write_all (io->output,
|
||||
(void *) buffer, (gsize) size,
|
||||
&bytes_written,
|
||||
NULL, &error))
|
||||
{
|
||||
g_printerr ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
written = bytes_written;
|
||||
|
||||
return (tsize_t) written;
|
||||
}
|
||||
|
||||
static GSeekType
|
||||
lseek_to_seek_type (int whence)
|
||||
{
|
||||
switch (whence)
|
||||
{
|
||||
default:
|
||||
case SEEK_SET:
|
||||
return G_SEEK_SET;
|
||||
case SEEK_CUR:
|
||||
return G_SEEK_CUR;
|
||||
case SEEK_END:
|
||||
return G_SEEK_END;
|
||||
}
|
||||
}
|
||||
|
||||
static toff_t
|
||||
tiff_io_seek (thandle_t handle,
|
||||
toff_t offset,
|
||||
int whence)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
GError *error = NULL;
|
||||
gboolean sought = FALSE;
|
||||
goffset position = -1;
|
||||
|
||||
sought = g_seekable_seek (G_SEEKABLE (io->stream),
|
||||
(goffset) offset, lseek_to_seek_type (whence),
|
||||
NULL, &error);
|
||||
if (sought)
|
||||
{
|
||||
position = g_seekable_tell (G_SEEKABLE (io->stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
return (toff_t) position;
|
||||
}
|
||||
|
||||
static int
|
||||
tiff_io_close (thandle_t handle)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
GError *error = NULL;
|
||||
gboolean closed = FALSE;
|
||||
|
||||
if (io->input)
|
||||
closed = g_input_stream_close (io->input, NULL, &error);
|
||||
else if (io->output)
|
||||
closed = g_output_stream_close (io->output, NULL, &error);
|
||||
|
||||
if (!closed)
|
||||
{
|
||||
g_printerr ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
g_clear_object (&io->stream);
|
||||
io->input = NULL;
|
||||
io->output = NULL;
|
||||
|
||||
g_clear_pointer (&io->buffer, g_free);
|
||||
|
||||
io->allocated = 0;
|
||||
io->used = 0;
|
||||
io->position = 0;
|
||||
|
||||
g_free (io);
|
||||
|
||||
return closed ? 0 : -1;
|
||||
}
|
||||
|
||||
static goffset
|
||||
input_stream_query_size (GInputStream *stream)
|
||||
{
|
||||
goffset size = 0;
|
||||
|
||||
while (G_IS_FILTER_INPUT_STREAM (stream))
|
||||
stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (stream));
|
||||
|
||||
if (G_IS_FILE_INPUT_STREAM (stream))
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (stream),
|
||||
G_FILE_ATTRIBUTE_STANDARD_SIZE,
|
||||
NULL,
|
||||
NULL);
|
||||
if (info)
|
||||
{
|
||||
size = g_file_info_get_size (info);
|
||||
g_object_unref (info);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static goffset
|
||||
output_stream_query_size (GOutputStream *stream)
|
||||
{
|
||||
goffset size = 0;
|
||||
|
||||
while (G_IS_FILTER_OUTPUT_STREAM (stream))
|
||||
stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (stream));
|
||||
|
||||
if (G_IS_FILE_OUTPUT_STREAM (stream))
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
info = g_file_output_stream_query_info (G_FILE_OUTPUT_STREAM (stream),
|
||||
G_FILE_ATTRIBUTE_STANDARD_SIZE,
|
||||
NULL,
|
||||
NULL);
|
||||
if (info)
|
||||
{
|
||||
size = g_file_info_get_size (info);
|
||||
g_object_unref (info);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static toff_t
|
||||
tiff_io_get_file_size (thandle_t handle)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
|
||||
if (io->input)
|
||||
return (toff_t) input_stream_query_size (io->input);
|
||||
else if (io->output)
|
||||
return (toff_t) output_stream_query_size (io->output);
|
||||
|
||||
return (toff_t) 0;
|
||||
}
|
||||
|
||||
static TIFF *
|
||||
tiff_open (gpointer stream,
|
||||
const gchar *mode,
|
||||
GError **error)
|
||||
{
|
||||
TIFFSetWarningHandler ((TIFFErrorHandler) tiff_io_warning);
|
||||
TIFFSetErrorHandler ((TIFFErrorHandler) tiff_io_error);
|
||||
TiffIO *io;
|
||||
|
||||
io = g_new0 (TiffIO, 1);
|
||||
|
||||
if (strcmp (mode, "r") == 0)
|
||||
{
|
||||
io->input = G_INPUT_STREAM (stream);
|
||||
io->stream = g_object_ref (G_OBJECT (stream));
|
||||
}
|
||||
else if (strcmp (mode, "w") == 0)
|
||||
{
|
||||
io->output = G_OUTPUT_STREAM (stream);
|
||||
io->stream = g_object_ref (G_OBJECT (stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TIFFClientOpen ("GTK", mode,
|
||||
(thandle_t) io,
|
||||
tiff_io_read,
|
||||
tiff_io_write,
|
||||
tiff_io_seek,
|
||||
tiff_io_close,
|
||||
tiff_io_get_file_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Public API */
|
||||
|
||||
static struct {
|
||||
GdkMemoryFormat format;
|
||||
guint16 bits_per_sample;
|
||||
guint16 samples_per_pixel;
|
||||
guint16 sample_format;
|
||||
} format_data[] = {
|
||||
{ GDK_MEMORY_DEFAULT, 8, 4, SAMPLEFORMAT_UINT },
|
||||
{ GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT },
|
||||
{ GDK_MEMORY_R16G16B16A16_PREMULTIPLIED, 16, 4, SAMPLEFORMAT_UINT },
|
||||
{ GDK_MEMORY_R16G16B16_FLOAT, 16, 3, SAMPLEFORMAT_IEEEFP },
|
||||
{ GDK_MEMORY_R32G32B32_FLOAT, 32, 3, SAMPLEFORMAT_IEEEFP },
|
||||
};
|
||||
|
||||
gboolean
|
||||
gdk_save_tiff (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GError **error)
|
||||
{
|
||||
TIFF *tif;
|
||||
guint16 bits_per_sample = 0;
|
||||
guint16 samples_per_pixel = 0;
|
||||
guint16 sample_format = 0;
|
||||
const guchar *line;
|
||||
|
||||
tif = tiff_open (stream, "w", error);
|
||||
if (!tif)
|
||||
return FALSE;
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (format_data); i++)
|
||||
{
|
||||
if (format == format_data[i].format)
|
||||
{
|
||||
bits_per_sample = format_data[i].bits_per_sample;
|
||||
samples_per_pixel = format_data[i].samples_per_pixel;
|
||||
sample_format = format_data[i].sample_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (bits_per_sample != 0);
|
||||
|
||||
TIFFSetField (tif, TIFFTAG_SOFTWARE, "GTK");
|
||||
TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, width);
|
||||
TIFFSetField (tif, TIFFTAG_IMAGELENGTH, height);
|
||||
TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
|
||||
TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel);
|
||||
TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, sample_format);
|
||||
TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||
TIFFSetField (tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
|
||||
// TODO: save gamma / colorspace
|
||||
|
||||
if (samples_per_pixel > 3)
|
||||
{
|
||||
guint16 extra_samples[] = { EXTRASAMPLE_ASSOCALPHA };
|
||||
TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
|
||||
}
|
||||
|
||||
TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
|
||||
TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
|
||||
line = (const guchar *)data;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (TIFFWriteScanline (tif, (void *)line, y, 0) == -1)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Writing data failed at row %d", y);
|
||||
TIFFClose (tif);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
line += stride;
|
||||
}
|
||||
|
||||
TIFFFlushData (tif);
|
||||
TIFFClose (tif);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
load_fallback (TIFF *tif,
|
||||
GError **error)
|
||||
{
|
||||
int width, height;
|
||||
guchar *pixels;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
|
||||
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width);
|
||||
TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &height);
|
||||
|
||||
pixels = g_malloc (width * height * 4);
|
||||
|
||||
if (!TIFFReadRGBAImageOriented (tif, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1))
|
||||
{
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Failed to load RGB data from TIFF file");
|
||||
g_free (pixels);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes = g_bytes_new_take (pixels, width * height * 4);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
bytes,
|
||||
width * 4);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* This isn't meant to be a very versatile tiff loader.
|
||||
* It just aims to load the subset that we're saving
|
||||
* ourselves, above.
|
||||
*/
|
||||
GdkTexture *
|
||||
gdk_load_tiff (GInputStream *stream,
|
||||
GError **error)
|
||||
{
|
||||
TIFF *tif;
|
||||
guint16 samples_per_pixel;
|
||||
guint16 bits_per_sample;
|
||||
guint16 photometric;
|
||||
guint16 planarconfig;
|
||||
guint16 sample_format;
|
||||
guint16 orientation;
|
||||
guint32 width, height;
|
||||
GdkMemoryFormat format;
|
||||
guchar *data, *line;
|
||||
gsize stride;
|
||||
int bpp;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
|
||||
tif = tiff_open (stream, "r", error);
|
||||
if (!tif)
|
||||
return NULL;
|
||||
|
||||
TIFFSetDirectory (tif, 0);
|
||||
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLEFORMAT, &sample_format);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_PHOTOMETRIC, &photometric);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_PLANARCONFIG, &planarconfig);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_ORIENTATION, &orientation);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_IMAGEWIDTH, &width);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_IMAGELENGTH, &height);
|
||||
|
||||
if (samples_per_pixel == 4)
|
||||
{
|
||||
guint16 extra;
|
||||
guint16 *extra_types;
|
||||
|
||||
if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
|
||||
extra = 0;
|
||||
|
||||
if (extra == 0 || extra_types[0] != EXTRASAMPLE_ASSOCALPHA)
|
||||
{
|
||||
texture = load_fallback (tif, error);
|
||||
TIFFClose (tif);
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
format = 0;
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (format_data); i++)
|
||||
{
|
||||
if (format_data[i].sample_format == sample_format &&
|
||||
format_data[i].bits_per_sample == bits_per_sample &&
|
||||
format_data[i].samples_per_pixel == samples_per_pixel)
|
||||
{
|
||||
format = format_data[i].format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (format == 0 ||
|
||||
photometric != PHOTOMETRIC_RGB ||
|
||||
planarconfig != PLANARCONFIG_CONTIG ||
|
||||
TIFFIsTiled (tif) ||
|
||||
orientation != ORIENTATION_TOPLEFT)
|
||||
{
|
||||
texture = load_fallback (tif, error);
|
||||
TIFFClose (tif);
|
||||
return texture;
|
||||
}
|
||||
|
||||
stride = width * gdk_memory_format_bytes_per_pixel (format);
|
||||
|
||||
g_assert (TIFFScanlineSize (tif) == stride);
|
||||
|
||||
data = g_new (guchar, height * stride);
|
||||
|
||||
line = data;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (TIFFReadScanline (tif, line, y, 0) == -1)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Reading data failed at row %d", y);
|
||||
TIFFClose (tif);
|
||||
g_free (data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
line += stride;
|
||||
}
|
||||
|
||||
bpp = gdk_memory_format_bytes_per_pixel (format);
|
||||
bytes = g_bytes_new_take (data, width * height * bpp);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
format,
|
||||
bytes, width * bpp);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Async code */
|
||||
|
||||
static void
|
||||
load_tiff_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GInputStream *stream = source_object;
|
||||
GdkTexture *texture;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_tiff (stream, &error);
|
||||
|
||||
if (texture)
|
||||
g_task_return_pointer (task, texture, g_object_unref);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_load_tiff_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_run_in_thread (task, load_tiff_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_tiff_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const guchar *data;
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
GdkMemoryFormat format;
|
||||
} SaveTiffData;
|
||||
|
||||
static void
|
||||
save_tiff_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GOutputStream *stream = source_object;
|
||||
SaveTiffData *data = task_data;
|
||||
GError *error = NULL;
|
||||
gboolean result;
|
||||
|
||||
result = gdk_save_tiff (stream,
|
||||
data->data,
|
||||
data->width,
|
||||
data->height,
|
||||
data->stride,
|
||||
data->format,
|
||||
&error);
|
||||
|
||||
if (result)
|
||||
g_task_return_boolean (task, result);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_save_tiff_async (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
SaveTiffData *save_data;
|
||||
|
||||
save_data = g_new0 (SaveTiffData, 1);
|
||||
save_data->data = data;
|
||||
save_data->width = width;
|
||||
save_data->height = height;
|
||||
save_data->stride = stride;
|
||||
save_data->format = format;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_set_task_data (task, save_data, g_free);
|
||||
g_task_run_in_thread (task, save_tiff_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_save_tiff_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
56
gdk/gdktiff.h
Normal file
56
gdk/gdktiff.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_TIFF_H__
|
||||
#define __GDK_TIFF_H__
|
||||
|
||||
#include "gdkmemorytexture.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
GdkTexture *gdk_load_tiff (GInputStream *stream,
|
||||
GError **error);
|
||||
|
||||
void gdk_load_tiff_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GdkTexture *gdk_load_tiff_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
gboolean gdk_save_tiff (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GError **error);
|
||||
|
||||
void gdk_save_tiff_async (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean gdk_save_tiff_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
#endif
|
@@ -50,6 +50,9 @@ gdk_public_sources = files([
|
||||
'gdktoplevelsize.c',
|
||||
'gdktoplevel.c',
|
||||
'gdkdragsurface.c',
|
||||
'gdkpng.c',
|
||||
'gdktiff.c',
|
||||
'gdkjpeg.c',
|
||||
])
|
||||
|
||||
gdk_public_headers = files([
|
||||
@@ -254,8 +257,8 @@ endif
|
||||
|
||||
libgdk = static_library('gdk',
|
||||
sources: [gdk_sources, gdk_backends_gen_headers, gdkconfig],
|
||||
dependencies: gdk_deps + [libgtk_css_dep],
|
||||
link_with: [libgtk_css, ],
|
||||
dependencies: gdk_deps + [libgtk_css_dep, png_dep, tiff_dep, jpeg_dep],
|
||||
link_with: [libgtk_css],
|
||||
include_directories: [confinc, gdkx11_inc, wlinc],
|
||||
c_args: libgdk_c_args + common_cflags,
|
||||
link_whole: gdk_backends,
|
||||
|
@@ -199,6 +199,7 @@ check_functions = [
|
||||
'mallinfo2',
|
||||
'sincos',
|
||||
'sincosf',
|
||||
'fopencookie',
|
||||
]
|
||||
|
||||
foreach func : check_functions
|
||||
@@ -389,6 +390,9 @@ pangocairo_dep = dependency('pangocairo', version: pango_req,
|
||||
pixbuf_dep = dependency('gdk-pixbuf-2.0', version: gdk_pixbuf_req,
|
||||
fallback : ['gdk-pixbuf', 'gdkpixbuf_dep'],
|
||||
default_options: ['png=enabled', 'jpeg=enabled', 'builtin_loaders=png,jpeg', 'man=false'])
|
||||
png_dep = dependency('libpng16')
|
||||
tiff_dep = dependency('libtiff-4')
|
||||
jpeg_dep = dependency('libjpeg')
|
||||
epoxy_dep = dependency('epoxy', version: epoxy_req,
|
||||
fallback: ['libepoxy', 'libepoxy_dep'])
|
||||
harfbuzz_dep = dependency('harfbuzz', version: '>= 2.1.0', required: false,
|
||||
|
@@ -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);
|
||||
@@ -186,6 +189,12 @@ main (int argc, char *argv[])
|
||||
|
||||
for (format = 0; format < GDK_MEMORY_N_FORMATS; format++)
|
||||
{
|
||||
if (format == GDK_MEMORY_R16G16B16_FLOAT ||
|
||||
format == GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED ||
|
||||
format == GDK_MEMORY_R32G32B32_FLOAT ||
|
||||
format == GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED)
|
||||
continue;
|
||||
|
||||
for (color = 0; color < N_COLORS; color++)
|
||||
{
|
||||
TestData *test_data = g_new (TestData, 1);
|
||||
|
Reference in New Issue
Block a user