/*
* 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)
{
}
/**
* gdk_memory_texture_new:
* @width: the width of the texture
* @height: the height of the texture
* @format: the format of the data
* @bytes: the #GBytes containing the pixel data
* @stride: rowstride for the data
*
* Creates a new texture for a blob of image data.
* The #GBytes must contain @stride x @height pixels
* in the given format.
*
* Returns: A newly-created #GdkTexture
*/
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);
}