texture: Add GdkMemoryTexture

GdkMemoryTexture is a texture implementation for holding data in memory
(read: GBytes). You specify the GdkMemoryFormat that data is in and off
you go.

Renderers can use this to add uploads in various different formats and
don't need to fallback to GDK doing the conersion on the CPU.

Supported formats can be extended if we need new ones, for now I just
added the relevant ones for Cairo and GdkPixbuf.

The constructor is also private still, because I'm not sure we want to
export GdkMemoryFormat.
Wrappers that do from_cairo_surface() and for_pixbuf() do exist though.
This commit is contained in:
Benjamin Otte 2018-03-06 03:41:13 +01:00
parent 160e6ad6f6
commit 8920639a2b
4 changed files with 387 additions and 161 deletions

262
gdk/gdkmemorytexture.c Normal file
View File

@ -0,0 +1,262 @@
/*
* Copyright © 2018 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gdkmemorytextureprivate.h"
struct _GdkMemoryTexture
{
GdkTexture parent_instance;
GdkMemoryFormat format;
GBytes *bytes;
gsize stride;
};
struct _GdkMemoryTextureClass
{
GdkTextureClass parent_class;
};
G_DEFINE_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK_TYPE_TEXTURE)
static void
gdk_memory_texture_dispose (GObject *object)
{
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (object);
g_clear_pointer (&self->bytes, g_bytes_unref);
G_OBJECT_CLASS (gdk_memory_texture_parent_class)->dispose (object);
}
static void
gdk_memory_texture_download (GdkTexture *texture,
guchar *data,
gsize stride)
{
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
gdk_memory_convert (data, stride,
GDK_MEMORY_CAIRO_FORMAT_ARGB32,
g_bytes_get_data (self->bytes, NULL), self->stride,
self->format,
texture->width, texture->height);
}
static void
gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
{
GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
texture_class->download = gdk_memory_texture_download;
gobject_class->dispose = gdk_memory_texture_dispose;
}
static void
gdk_memory_texture_init (GdkMemoryTexture *self)
{
}
GdkTexture *
gdk_memory_texture_new (int width,
int height,
GdkMemoryFormat format,
GBytes *bytes,
gsize stride)
{
GdkMemoryTexture *self;
self = g_object_new (GDK_TYPE_MEMORY_TEXTURE,
"width", width,
"height", height,
NULL);
self->format = format;
self->bytes = g_bytes_ref (bytes);
self->stride = stride;
return GDK_TEXTURE (self);
}
GdkMemoryFormat
gdk_memory_texture_get_format (GdkMemoryTexture *self)
{
return self->format;
}
const guchar *
gdk_memory_texture_get_data (GdkMemoryTexture *self)
{
return g_bytes_get_data (self->bytes, NULL);
}
gsize
gdk_memory_texture_get_stride (GdkMemoryTexture *self)
{
return self->stride;
}
static void
convert_memcpy (guchar *dest_data,
gsize dest_stride,
const guchar *src_data,
gsize src_stride,
gsize width,
gsize height)
{
gsize y;
for (y = 0; y < height; y++)
memcpy (dest_data + y * dest_stride, src_data + y * src_stride, 4 * width);
}
#define SWIZZLE(A,R,G,B) \
static void \
convert_swizzle ## A ## R ## G ## B (guchar *dest_data, \
gsize dest_stride, \
const guchar *src_data, \
gsize src_stride, \
gsize width, \
gsize height) \
{ \
gsize x, y; \
\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
dest_data[4 * x + A] = src_data[4 * x + 0]; \
dest_data[4 * x + R] = src_data[4 * x + 1]; \
dest_data[4 * x + G] = src_data[4 * x + 2]; \
dest_data[4 * x + B] = src_data[4 * x + 3]; \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}
SWIZZLE(3,2,1,0)
#define SWIZZLE_OPAQUE(A,R,G,B) \
static void \
convert_swizzle_opaque_## A ## R ## G ## B (guchar *dest_data, \
gsize dest_stride, \
const guchar *src_data, \
gsize src_stride, \
gsize width, \
gsize height) \
{ \
gsize x, y; \
\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
dest_data[4 * x + A] = 0xFF; \
dest_data[4 * x + R] = src_data[3 * x + 0]; \
dest_data[4 * x + G] = src_data[3 * x + 1]; \
dest_data[4 * x + B] = src_data[3 * x + 2]; \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}
SWIZZLE_OPAQUE(3,2,1,0)
SWIZZLE_OPAQUE(3,0,1,2)
SWIZZLE_OPAQUE(0,1,2,3)
SWIZZLE_OPAQUE(0,3,2,1)
#define PREMULTIPLY(d,c,a) G_STMT_START { guint t = c * a + 0x80; d = ((t >> 8) + t) >> 8; } G_STMT_END
#define SWIZZLE_PREMULTIPLY(A,R,G,B, A2,R2,G2,B2) \
static void \
convert_swizzle_premultiply_ ## A ## R ## G ## B ## _ ## A2 ## R2 ## G2 ## B2 \
(guchar *dest_data, \
gsize dest_stride, \
const guchar *src_data, \
gsize src_stride, \
gsize width, \
gsize height) \
{ \
gsize x, y; \
\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
dest_data[4 * x + A] = src_data[4 * x + A2]; \
PREMULTIPLY(dest_data[4 * x + R], src_data[4 * x + R2], src_data[4 * x + A2]); \
PREMULTIPLY(dest_data[4 * x + G], src_data[4 * x + G2], src_data[4 * x + A2]); \
PREMULTIPLY(dest_data[4 * x + B], src_data[4 * x + B2], src_data[4 * x + A2]); \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}
SWIZZLE_PREMULTIPLY (3,2,1,0, 3,2,1,0)
SWIZZLE_PREMULTIPLY (0,1,2,3, 3,2,1,0)
SWIZZLE_PREMULTIPLY (3,2,1,0, 0,1,2,3)
SWIZZLE_PREMULTIPLY (0,1,2,3, 0,1,2,3)
SWIZZLE_PREMULTIPLY (3,2,1,0, 3,0,1,2)
SWIZZLE_PREMULTIPLY (0,1,2,3, 3,0,1,2)
SWIZZLE_PREMULTIPLY (3,2,1,0, 0,3,2,1)
SWIZZLE_PREMULTIPLY (0,1,2,3, 0,3,2,1)
typedef void (* ConversionFunc) (guchar *dest_data,
gsize dest_stride,
const guchar *src_data,
gsize src_stride,
gsize width,
gsize height);
static ConversionFunc converters[GDK_MEMORY_N_FORMATS][2] =
{
{ convert_memcpy, convert_swizzle3210 },
{ convert_swizzle3210, convert_memcpy },
{ convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210 },
{ convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123 },
{ convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012 },
{ convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321 },
{ convert_swizzle_opaque_3210, convert_swizzle_opaque_3012 },
{ convert_swizzle_opaque_0123, convert_swizzle_opaque_0321 }
};
void
gdk_memory_convert (guchar *dest_data,
gsize dest_stride,
GdkMemoryFormat dest_format,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
gsize width,
gsize height)
{
g_assert (dest_format < 2);
g_assert (src_format < GDK_MEMORY_N_FORMATS);
converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height);
}

View File

@ -0,0 +1,91 @@
/*
* Copyright © 2018 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GDK_MEMORY_TEXTURE_PRIVATE_H__
#define __GDK_MEMORY_TEXTURE_PRIVATE_H__
#include "gdktextureprivate.h"
G_BEGIN_DECLS
/*
* GdkMemoryFormat:
*
* #GdkMemroyFormat describes a format that bytes can have in memory.
*
* It describes formats by listing the contents of the memory passed to it.
* So GDK_MEMORY_A8R8G8B8 will be 8 bits of alpha, followed by 8 bites of each
* blue, green and red. It is not endian-dependant, so CAIRO_FORMAT_ARGB32 is
* represented by 2 different GdkMemoryFormats.
*
* Its naming is modelled after VkFormat (see
* https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VkFormat
* for details).
*/
typedef enum {
GDK_MEMORY_B8G8R8A8_PREMULTIPLIED,
GDK_MEMORY_A8R8G8B8_PREMULTIPLIED,
GDK_MEMORY_B8G8R8A8,
GDK_MEMORY_A8R8G8B8,
GDK_MEMORY_R8G8B8A8,
GDK_MEMORY_A8B8G8R8,
GDK_MEMORY_R8G8B8,
GDK_MEMORY_B8G8R8,
GDK_MEMORY_N_FORMATS
} GdkMemoryFormat;
#define GDK_MEMORY_GDK_PIXBUF_OPAQUE GDK_MEMORY_R8G8B8
#define GDK_MEMORY_GDK_PIXBUF_ALPHA GDK_MEMORY_R8G8B8A8
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define GDK_MEMORY_CAIRO_FORMAT_ARGB32 GDK_MEMORY_B8G8R8A8_PREMULTIPLIED
#elif G_BYTE_ORDER == G_BIG_ENDIAN
#define GDK_MEMORY_CAIRO_FORMAT_ARGB32 GDK_MEMORY_A8R8G8B8_PREMULTIPLIED
#else
#error "Unknown byte order."
#endif
#define GDK_TYPE_MEMORY_TEXTURE (gdk_memory_texture_get_type ())
G_DECLARE_FINAL_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK, MEMORY_TEXTURE, GdkTexture)
GdkTexture * gdk_memory_texture_new (int width,
int height,
GdkMemoryFormat format,
GBytes *bytes,
gsize stride);
GdkMemoryFormat gdk_memory_texture_get_format (GdkMemoryTexture *self);
const guchar * gdk_memory_texture_get_data (GdkMemoryTexture *self);
gsize gdk_memory_texture_get_stride (GdkMemoryTexture *self);
void gdk_memory_convert (guchar *dest_data,
gsize dest_stride,
GdkMemoryFormat dest_format,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
gsize width,
gsize height);
G_END_DECLS
#endif /* __GDK_MEMORY_TEXTURE_PRIVATE_H__ */

View File

@ -36,8 +36,8 @@
#include "gdktextureprivate.h"
#include "gdkcairo.h"
#include "gdkinternals.h"
#include "gdkmemorytextureprivate.h"
/**
* SECTION:gdktexture
@ -211,82 +211,6 @@ gdk_texture_init (GdkTexture *self)
{
}
/* GdkCairoTexture */
#define GDK_TYPE_CAIRO_TEXTURE (gdk_cairo_texture_get_type ())
G_DECLARE_FINAL_TYPE (GdkCairoTexture, gdk_cairo_texture, GDK, CAIRO_TEXTURE, GdkTexture)
struct _GdkCairoTexture {
GdkTexture parent_instance;
cairo_surface_t *surface;
};
struct _GdkCairoTextureClass {
GdkTextureClass parent_class;
};
G_DEFINE_TYPE (GdkCairoTexture, gdk_cairo_texture, GDK_TYPE_TEXTURE)
static void
gdk_cairo_texture_finalize (GObject *object)
{
GdkCairoTexture *self = GDK_CAIRO_TEXTURE (object);
cairo_surface_destroy (self->surface);
G_OBJECT_CLASS (gdk_cairo_texture_parent_class)->finalize (object);
}
static cairo_surface_t *
gdk_cairo_texture_download_surface (GdkTexture *texture)
{
GdkCairoTexture *self = GDK_CAIRO_TEXTURE (texture);
return cairo_surface_reference (self->surface);
}
static void
gdk_cairo_texture_download (GdkTexture *texture,
guchar *data,
gsize stride)
{
GdkCairoTexture *self = GDK_CAIRO_TEXTURE (texture);
cairo_surface_t *surface;
cairo_t *cr;
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
texture->width, texture->height,
stride);
cr = cairo_create (surface);
cairo_set_source_surface (cr, self->surface, 0, 0);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
cairo_destroy (cr);
cairo_surface_finish (surface);
cairo_surface_destroy (surface);
}
static void
gdk_cairo_texture_class_init (GdkCairoTextureClass *klass)
{
GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
texture_class->download = gdk_cairo_texture_download;
texture_class->download_surface = gdk_cairo_texture_download_surface;
gobject_class->finalize = gdk_cairo_texture_finalize;
}
static void
gdk_cairo_texture_init (GdkCairoTexture *self)
{
}
/**
* gdk_texture_new_for_data:
* @data: (array): the pixel data
@ -341,90 +265,28 @@ gdk_texture_new_for_data (const guchar *data,
GdkTexture *
gdk_texture_new_for_surface (cairo_surface_t *surface)
{
GdkCairoTexture *texture;
GdkTexture *texture;
GBytes *bytes;
g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
g_return_val_if_fail (cairo_image_surface_get_width (surface) > 0, NULL);
g_return_val_if_fail (cairo_image_surface_get_height (surface) > 0, NULL);
texture = g_object_new (GDK_TYPE_CAIRO_TEXTURE,
"width", cairo_image_surface_get_width (surface),
"height", cairo_image_surface_get_height (surface),
NULL);
bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
cairo_image_surface_get_height (surface)
* cairo_image_surface_get_stride (surface),
(GDestroyNotify) cairo_surface_destroy,
cairo_surface_reference (surface));
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
GDK_MEMORY_CAIRO_FORMAT_ARGB32,
bytes,
cairo_image_surface_get_stride (surface));
texture->surface = cairo_surface_reference (surface);
g_bytes_unref (bytes);
return (GdkTexture *) texture;
}
/* GdkPixbufTexture */
#define GDK_TYPE_PIXBUF_TEXTURE (gdk_pixbuf_texture_get_type ())
G_DECLARE_FINAL_TYPE (GdkPixbufTexture, gdk_pixbuf_texture, GDK, PIXBUF_TEXTURE, GdkTexture)
struct _GdkPixbufTexture {
GdkTexture parent_instance;
GdkPixbuf *pixbuf;
};
struct _GdkPixbufTextureClass {
GdkTextureClass parent_class;
};
G_DEFINE_TYPE (GdkPixbufTexture, gdk_pixbuf_texture, GDK_TYPE_TEXTURE)
static void
gdk_pixbuf_texture_finalize (GObject *object)
{
GdkPixbufTexture *self = GDK_PIXBUF_TEXTURE (object);
g_object_unref (self->pixbuf);
G_OBJECT_CLASS (gdk_pixbuf_texture_parent_class)->finalize (object);
}
static void
gdk_pixbuf_texture_download (GdkTexture *texture,
guchar *data,
gsize stride)
{
GdkPixbufTexture *self = GDK_PIXBUF_TEXTURE (texture);
cairo_surface_t *surface;
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32,
texture->width, texture->height,
stride);
gdk_cairo_surface_paint_pixbuf (surface, self->pixbuf);
cairo_surface_finish (surface);
cairo_surface_destroy (surface);
}
static cairo_surface_t *
gdk_pixbuf_texture_download_surface (GdkTexture *texture)
{
GdkPixbufTexture *self = GDK_PIXBUF_TEXTURE (texture);
return gdk_cairo_surface_create_from_pixbuf (self->pixbuf, 1, NULL);
}
static void
gdk_pixbuf_texture_class_init (GdkPixbufTextureClass *klass)
{
GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
texture_class->download = gdk_pixbuf_texture_download;
texture_class->download_surface = gdk_pixbuf_texture_download_surface;
gobject_class->finalize = gdk_pixbuf_texture_finalize;
}
static void
gdk_pixbuf_texture_init (GdkPixbufTexture *self)
{
return texture;
}
/**
@ -438,18 +300,28 @@ gdk_pixbuf_texture_init (GdkPixbufTexture *self)
GdkTexture *
gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf)
{
GdkPixbufTexture *self;
GdkTexture *texture;
GBytes *bytes;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
self = g_object_new (GDK_TYPE_PIXBUF_TEXTURE,
"width", gdk_pixbuf_get_width (pixbuf),
"height", gdk_pixbuf_get_height (pixbuf),
NULL);
bytes = g_bytes_new_with_free_func (gdk_pixbuf_get_pixels (pixbuf),
gdk_pixbuf_get_height (pixbuf)
* gdk_pixbuf_get_rowstride (pixbuf),
g_object_unref,
g_object_ref (pixbuf));
texture = gdk_memory_texture_new (gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
gdk_pixbuf_get_has_alpha (pixbuf)
? GDK_MEMORY_GDK_PIXBUF_ALPHA
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
bytes,
gdk_pixbuf_get_rowstride (pixbuf));
self->pixbuf = g_object_ref (pixbuf);
g_bytes_unref (bytes);
return GDK_TEXTURE (self);
return texture;
}
/**

View File

@ -27,6 +27,7 @@ gdk_public_sources = files([
'gdkgltexture.c',
'gdkkeys.c',
'gdkkeyuni.c',
'gdkmemorytexture.c',
'gdkmonitor.c',
'gdkpango.c',
'gdkpixbuf-drawable.c',