diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c
new file mode 100644
index 0000000000..99c2e4a0d9
--- /dev/null
+++ b/gdk/gdkmemorytexture.c
@@ -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 .
+ *
+ * Authors: Benjamin Otte
+ */
+
+#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);
+}
diff --git a/gdk/gdkmemorytextureprivate.h b/gdk/gdkmemorytextureprivate.h
new file mode 100644
index 0000000000..540ec26a0f
--- /dev/null
+++ b/gdk/gdkmemorytextureprivate.h
@@ -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 .
+ *
+ * Authors: Benjamin Otte
+ */
+
+#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__ */
diff --git a/gdk/gdktexture.c b/gdk/gdktexture.c
index 8cfd0abc60..616b0cbb8b 100644
--- a/gdk/gdktexture.c
+++ b/gdk/gdktexture.c
@@ -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;
}
/**
diff --git a/gdk/meson.build b/gdk/meson.build
index 760cad8b64..10d2c4e698 100644
--- a/gdk/meson.build
+++ b/gdk/meson.build
@@ -27,6 +27,7 @@ gdk_public_sources = files([
'gdkgltexture.c',
'gdkkeys.c',
'gdkkeyuni.c',
+ 'gdkmemorytexture.c',
'gdkmonitor.c',
'gdkpango.c',
'gdkpixbuf-drawable.c',