/* * 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 gsize gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format) { switch (format) { 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: return 4; case GDK_MEMORY_R8G8B8: case GDK_MEMORY_B8G8R8: return 3; case GDK_MEMORY_N_FORMATS: default: g_assert_not_reached (); return 4; } } 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, const GdkRectangle *area, guchar *data, gsize stride) { GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture); gdk_memory_convert (data, stride, GDK_MEMORY_CAIRO_FORMAT_ARGB32, (guchar *) g_bytes_get_data (self->bytes, NULL) + area->x * gdk_memory_format_bytes_per_pixel (self->format) + area->y * self->stride, self->stride, self->format, area->width, area->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_0123 }, { convert_swizzle_opaque_3012, 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); }