mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-29 15:01:23 +00:00
593907f7b0
Copy the format conversion code from GdkMemoryTexture so we can produce all formats, and test them all. The upload fast paths assume that the stride is a multiple of four, so some of the padding values cause it to fail. Apart from that, things seem to work for all combinations.
400 lines
14 KiB
C
400 lines
14 KiB
C
#include <gtk/gtk.h>
|
|
|
|
static const char *format_name[] = {
|
|
"BGRAp", "ARGBp", "RGBAp",
|
|
"BGRA", "ARGB", "RGBA", "ABGR",
|
|
"RGB", "BGR",
|
|
};
|
|
|
|
static const char *
|
|
format_to_string (GdkMemoryFormat format)
|
|
{
|
|
if (format < GDK_MEMORY_N_FORMATS)
|
|
return format_name[format];
|
|
else
|
|
return "ERROR";
|
|
}
|
|
|
|
/* Copied from gdkmemorytexture.c */
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
convert_memcpy3 (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, 3 * width);
|
|
}
|
|
|
|
#define SWIZZLE3(R,G,B) \
|
|
static void \
|
|
convert_swizzle ## 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[3 * x + R] = src_data[3 * x + 0]; \
|
|
dest_data[3 * x + G] = src_data[3 * x + 1]; \
|
|
dest_data[3 * x + B] = src_data[3 * x + 2]; \
|
|
} \
|
|
\
|
|
dest_data += dest_stride; \
|
|
src_data += src_stride; \
|
|
} \
|
|
}
|
|
|
|
SWIZZLE3(2,1,0)
|
|
|
|
#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)
|
|
SWIZZLE(2,1,0,3)
|
|
SWIZZLE(3,0,1,2)
|
|
SWIZZLE(1,2,3,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)
|
|
SWIZZLE_PREMULTIPLY (3,0,1,2, 3,2,1,0)
|
|
SWIZZLE_PREMULTIPLY (3,0,1,2, 0,1,2,3)
|
|
SWIZZLE_PREMULTIPLY (3,0,1,2, 3,0,1,2)
|
|
SWIZZLE_PREMULTIPLY (3,0,1,2, 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][GDK_MEMORY_N_FORMATS] =
|
|
{
|
|
{ convert_memcpy, convert_swizzle3210, convert_swizzle2103, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ convert_swizzle3210, convert_memcpy, convert_swizzle3012, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ convert_swizzle2103, convert_swizzle1230, convert_memcpy, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210, convert_swizzle_premultiply_3012_3210, convert_memcpy, NULL, NULL, NULL, NULL, NULL },
|
|
{ convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123, convert_swizzle_premultiply_3012_0123, NULL, convert_memcpy, NULL, NULL, NULL, NULL },
|
|
{ convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012, convert_swizzle_premultiply_3012_3012, convert_swizzle2103, convert_swizzle1230, convert_memcpy, convert_swizzle3210, NULL, NULL },
|
|
{ convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321, convert_swizzle_premultiply_3012_0321, NULL, NULL, NULL, convert_memcpy, NULL, NULL },
|
|
{ convert_swizzle_opaque_3210, convert_swizzle_opaque_0123, convert_swizzle_opaque_3012, NULL, NULL, NULL, NULL, convert_memcpy3, convert_swizzle210 },
|
|
{ convert_swizzle_opaque_3012, convert_swizzle_opaque_0321, convert_swizzle_opaque_3210, NULL, NULL, NULL, NULL, convert_swizzle210, convert_memcpy3 },
|
|
};
|
|
|
|
static 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 < GDK_MEMORY_N_FORMATS);
|
|
g_assert (src_format < GDK_MEMORY_N_FORMATS);
|
|
|
|
if (converters[src_format][dest_format] == NULL)
|
|
g_error ("Conversion from %s to %s not supported", format_to_string (src_format), format_to_string (dest_format));
|
|
|
|
converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height);
|
|
}
|
|
|
|
/* End of copied code */
|
|
|
|
static GdkTexture *
|
|
make_texture (GdkMemoryFormat format,
|
|
int padding,
|
|
int *out_stride,
|
|
int *out_bpp)
|
|
{
|
|
GdkPixbuf *source;
|
|
GdkMemoryFormat source_format;
|
|
int width, height, stride, bpp;
|
|
guchar *data;
|
|
GBytes *bytes;
|
|
GdkTexture *texture;
|
|
GError *error = NULL;
|
|
|
|
width = height = 200;
|
|
|
|
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
|
|
width, height, TRUE,
|
|
&error);
|
|
if (!source)
|
|
g_error ("%s", error->message);
|
|
|
|
source_format = GDK_MEMORY_R8G8B8;
|
|
bpp = 3;
|
|
|
|
if (format != GDK_MEMORY_R8G8B8 && format != GDK_MEMORY_B8G8R8)
|
|
{
|
|
bpp = 4;
|
|
|
|
/* add an alpha channel with 50% alpha */
|
|
GdkPixbuf *pb = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
|
|
gdk_pixbuf_composite (source, pb, 0, 0, width, height, 0, 0, 1, 1, GDK_INTERP_BILINEAR, 128);
|
|
g_object_unref (source);
|
|
source = pb;
|
|
|
|
source_format = GDK_MEMORY_R8G8B8A8;
|
|
}
|
|
|
|
stride = bpp * width + padding;
|
|
data = g_new0 (guchar, stride * height);
|
|
|
|
gdk_memory_convert (data, stride, format,
|
|
gdk_pixbuf_get_pixels (source),
|
|
gdk_pixbuf_get_rowstride (source),
|
|
source_format,
|
|
width, height);
|
|
|
|
g_object_unref (source);
|
|
|
|
bytes = g_bytes_new_take (data, stride * height);
|
|
texture = gdk_memory_texture_new (width, height, format, bytes, stride);
|
|
g_bytes_unref (bytes);
|
|
|
|
*out_stride = stride;
|
|
*out_bpp = bpp;
|
|
|
|
return texture;
|
|
}
|
|
|
|
static void
|
|
update_picture (GtkWidget *picture)
|
|
{
|
|
GdkMemoryFormat format;
|
|
int padding;
|
|
GdkTexture *texture;
|
|
GtkLabel *label;
|
|
char *text;
|
|
int stride;
|
|
int bpp;
|
|
|
|
format = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (picture), "format"));
|
|
padding = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (picture), "padding"));
|
|
|
|
texture = make_texture (format, padding, &stride, &bpp);
|
|
gtk_picture_set_paintable (GTK_PICTURE (picture), GDK_PAINTABLE (texture));
|
|
|
|
label = GTK_LABEL (g_object_get_data (G_OBJECT (picture), "size_label"));
|
|
text = g_strdup_printf ("%d x %d @ %d",
|
|
gdk_texture_get_width (texture),
|
|
gdk_texture_get_height (texture),
|
|
bpp);
|
|
gtk_label_set_label (label, text);
|
|
g_free (text);
|
|
|
|
label = GTK_LABEL (g_object_get_data (G_OBJECT (picture), "stride_label"));
|
|
text = g_strdup_printf ("%d", stride);
|
|
gtk_label_set_label (label, text);
|
|
g_free (text);
|
|
|
|
g_object_unref (texture);
|
|
}
|
|
|
|
static void
|
|
update_format (GtkDropDown *dropdown,
|
|
GParamSpec *pspec,
|
|
GtkWidget *picture)
|
|
{
|
|
g_object_set_data (G_OBJECT (picture), "format", GINT_TO_POINTER (gtk_drop_down_get_selected (dropdown)));
|
|
update_picture (picture);
|
|
}
|
|
|
|
static void
|
|
update_padding (GtkSpinButton *spinbutton,
|
|
GParamSpec *pspec,
|
|
GtkWidget *picture)
|
|
{
|
|
g_object_set_data (G_OBJECT (picture), "padding", GINT_TO_POINTER (gtk_spin_button_get_value_as_int (spinbutton)));
|
|
update_picture (picture);
|
|
}
|
|
|
|
static void
|
|
add_to_grid (GtkWidget *grid,
|
|
int left,
|
|
int top,
|
|
GdkMemoryFormat format,
|
|
int padding)
|
|
{
|
|
GtkWidget *dropdown, *spin, *picture, *label;
|
|
|
|
picture = gtk_picture_new ();
|
|
gtk_grid_attach (GTK_GRID (grid), picture, left + 2, top + 0, 1, 4);
|
|
|
|
g_object_set_data (G_OBJECT (picture), "format", GINT_TO_POINTER (format));
|
|
g_object_set_data (G_OBJECT (picture), "padding", GINT_TO_POINTER (padding));
|
|
|
|
dropdown = gtk_drop_down_new_from_strings (format_name);
|
|
gtk_widget_set_valign (dropdown, GTK_ALIGN_CENTER);
|
|
gtk_drop_down_set_selected (GTK_DROP_DOWN (dropdown), format);
|
|
g_signal_connect (dropdown, "notify::selected", G_CALLBACK (update_format), picture);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Format"), left, top, 1, 1);
|
|
gtk_grid_attach (GTK_GRID (grid), dropdown, left + 1, top, 1, 1);
|
|
|
|
spin = gtk_spin_button_new_with_range (0, 10, 1);
|
|
gtk_widget_set_valign (spin, GTK_ALIGN_CENTER);
|
|
gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), padding);
|
|
g_signal_connect (spin, "notify::value", G_CALLBACK (update_padding), picture);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Padding"), left, top + 1, 1, 1);
|
|
gtk_grid_attach (GTK_GRID (grid), spin, left + 1, top + 1, 1, 1);
|
|
|
|
label = gtk_label_new ("");
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
|
g_object_set_data (G_OBJECT (picture), "size_label", label);
|
|
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Size"), left, top + 2, 1, 1);
|
|
gtk_grid_attach (GTK_GRID (grid), label, left + 1, top + 2, 1, 1);
|
|
label = gtk_label_new ("");
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
|
g_object_set_data (G_OBJECT (picture), "stride_label", label);
|
|
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Stride"), left, top + 3, 1, 1);
|
|
gtk_grid_attach (GTK_GRID (grid), label, left + 1, top + 3, 1, 1);
|
|
|
|
update_picture (picture);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *window, *grid;
|
|
|
|
gtk_init ();
|
|
|
|
window = gtk_window_new ();
|
|
grid = gtk_grid_new ();
|
|
gtk_widget_set_margin_top (grid, 10);
|
|
gtk_widget_set_margin_bottom (grid, 10);
|
|
gtk_widget_set_margin_start (grid, 10);
|
|
gtk_widget_set_margin_end (grid, 10);
|
|
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
|
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
|
gtk_window_set_child (GTK_WINDOW (window), grid);
|
|
|
|
add_to_grid (grid, 0, 0, GDK_MEMORY_R8G8B8, 0);
|
|
|
|
gtk_window_present (GTK_WINDOW (window));
|
|
|
|
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
return 0;
|
|
}
|