testupload: Test more formats

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.
This commit is contained in:
Matthias Clasen 2020-09-26 17:26:12 -04:00
parent 34ec226852
commit 593907f7b0

View File

@ -1,49 +1,380 @@
#include <gtk/gtk.h>
static void
add_to_grid (GtkWidget *grid,
int left,
int top,
int n_channels,
gboolean premul,
int width,
int height,
int stride,
GtkWidget *picture)
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;
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Channels"), left, top, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Width"), left, top + 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Height"), left, top + 2, 1, 1);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Stride"), left, top + 3, 1, 1);
format = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (picture), "format"));
padding = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (picture), "padding"));
text = g_strdup_printf ("%d%s", n_channels, premul ? " (premul)" : "");
gtk_grid_attach (GTK_GRID (grid), gtk_label_new (text), left + 1, top, 1, 1);
g_free (text);
text = g_strdup_printf ("%d", width);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new (text), left + 1, top + 1, 1, 1);
g_free (text);
text = g_strdup_printf ("%d", height);
gtk_grid_attach (GTK_GRID (grid), gtk_label_new (text), left + 1, top + 2, 1, 1);
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_grid_attach (GTK_GRID (grid), gtk_label_new (text), left + 1, top + 3, 1, 1);
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;
GdkPixbuf *source, *pb, *pb2;
GError *error = NULL;
cairo_t *cr;
cairo_surface_t *surface;
GBytes *bytes;
GdkTexture *texture;
gtk_init ();
@ -53,220 +384,11 @@ main (int argc, char *argv[])
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), 10);
gtk_grid_set_column_spacing (GTK_GRID (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);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
200, 200, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
add_to_grid (grid, 0, 0,
gdk_pixbuf_get_n_channels (source),
FALSE,
gdk_pixbuf_get_width (source),
gdk_pixbuf_get_height (source),
gdk_pixbuf_get_rowstride (source),
gtk_picture_new_for_pixbuf (source));
g_object_unref (source);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
199, 199, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
add_to_grid (grid, 4, 0,
gdk_pixbuf_get_n_channels (source),
FALSE,
gdk_pixbuf_get_width (source),
gdk_pixbuf_get_height (source),
gdk_pixbuf_get_rowstride (source),
gtk_picture_new_for_pixbuf (source));
g_object_unref (source);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
201, 201, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
pb = gdk_pixbuf_new_subpixbuf (source, 0, 0, 200, 200);
add_to_grid (grid, 8, 0,
gdk_pixbuf_get_n_channels (pb),
FALSE,
gdk_pixbuf_get_width (pb),
gdk_pixbuf_get_height (pb),
gdk_pixbuf_get_rowstride (pb),
gtk_picture_new_for_pixbuf (pb));
g_object_unref (source);
g_object_unref (pb);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
200, 200, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
pb = gdk_pixbuf_add_alpha (source, FALSE, 0, 0, 0);
add_to_grid (grid, 0, 5,
gdk_pixbuf_get_n_channels (pb),
FALSE,
gdk_pixbuf_get_width (pb),
gdk_pixbuf_get_height (pb),
gdk_pixbuf_get_rowstride (pb),
gtk_picture_new_for_pixbuf (pb));
g_object_unref (source);
g_object_unref (pb);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
199, 199, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
pb = gdk_pixbuf_add_alpha (source, FALSE, 0, 0, 0);
add_to_grid (grid, 4, 5,
gdk_pixbuf_get_n_channels (pb),
FALSE,
gdk_pixbuf_get_width (pb),
gdk_pixbuf_get_height (pb),
gdk_pixbuf_get_rowstride (pb),
gtk_picture_new_for_pixbuf (pb));
g_object_unref (source);
g_object_unref (pb);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
201, 201, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
pb = gdk_pixbuf_add_alpha (source, FALSE, 0, 0, 0);
pb2 = gdk_pixbuf_new_subpixbuf (pb, 0, 0, 200, 200);
add_to_grid (grid, 8, 5,
gdk_pixbuf_get_n_channels (pb2),
FALSE,
gdk_pixbuf_get_width (pb2),
gdk_pixbuf_get_height (pb2),
gdk_pixbuf_get_rowstride (pb2),
gtk_picture_new_for_pixbuf (pb2));
g_object_unref (source);
g_object_unref (pb);
g_object_unref (pb2);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
200, 200, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
pb = gdk_pixbuf_add_alpha (source, FALSE, 0, 0, 0);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width (pb), gdk_pixbuf_get_height (pb));
cr = cairo_create (surface);
gdk_cairo_set_source_pixbuf (cr, pb, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
bytes = g_bytes_new (cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface) *
cairo_image_surface_get_height (surface));
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
GDK_MEMORY_DEFAULT,
bytes,
cairo_image_surface_get_stride (surface));
g_bytes_unref (bytes);
add_to_grid (grid, 0, 10,
4, TRUE,
cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
cairo_image_surface_get_stride (surface),
gtk_picture_new_for_paintable (GDK_PAINTABLE (texture)));
g_object_unref (source);
g_object_unref (pb);
cairo_surface_destroy (surface);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
199, 199, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
pb = gdk_pixbuf_add_alpha (source, FALSE, 0, 0, 0);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width (pb), gdk_pixbuf_get_height (pb));
cr = cairo_create (surface);
gdk_cairo_set_source_pixbuf (cr, pb, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
bytes = g_bytes_new (cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface) *
cairo_image_surface_get_height (surface));
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
GDK_MEMORY_DEFAULT,
bytes,
cairo_image_surface_get_stride (surface));
g_bytes_unref (bytes);
add_to_grid (grid, 4, 10,
4, TRUE,
cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
cairo_image_surface_get_stride (surface),
gtk_picture_new_for_paintable (GDK_PAINTABLE (texture)));
g_object_unref (source);
g_object_unref (pb);
cairo_surface_destroy (surface);
source = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg",
201, 201, TRUE,
&error);
if (!source)
g_error ("%s", error->message);
pb = gdk_pixbuf_add_alpha (source, FALSE, 0, 0, 0);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width (pb), gdk_pixbuf_get_height (pb));
cr = cairo_create (surface);
gdk_cairo_set_source_pixbuf (cr, pb, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
bytes = g_bytes_new (cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface) *
cairo_image_surface_get_height (surface));
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface) - 1,
cairo_image_surface_get_height (surface) - 1,
GDK_MEMORY_DEFAULT,
bytes,
cairo_image_surface_get_stride (surface));
g_bytes_unref (bytes);
add_to_grid (grid, 8, 10,
4, TRUE,
gdk_texture_get_width (texture),
gdk_texture_get_height (texture),
cairo_image_surface_get_stride (surface),
gtk_picture_new_for_paintable (GDK_PAINTABLE (texture)));
g_object_unref (source);
g_object_unref (pb);
cairo_surface_destroy (surface);
add_to_grid (grid, 0, 0, GDK_MEMORY_R8G8B8, 0);
gtk_window_present (GTK_WINDOW (window));