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',