From 94a2dc4c479275569cd470a24e40214aff5f6e11 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 29 May 2023 01:15:33 +0200 Subject: [PATCH] testsuite: Update memorytexture test for TextureDownloader There is no longer a need to use gdk_texture_download() and force conversion to ARGB8 format. We can download the pixels in the original format again. That way we avoid testing the conversion code and avoid having to deal with differences in representable colors. However, some formats do do conversions, so we allow pixel comparisons to be accurate (requires 16bit comparison accuracy) or inaccurate (we only care about 8bit). Note that for the default RGBA formats, this is identical and means they need to be bit-exact the same, no matter what. But the higher bit depth formats may be more different - floating point can even have different values with high accuracy (the float mantissa is 23 bit, we only care about 16). --- testsuite/gdk/memorytexture.c | 234 ++++++++++++++++++++++++++-------- 1 file changed, 182 insertions(+), 52 deletions(-) diff --git a/testsuite/gdk/memorytexture.c b/testsuite/gdk/memorytexture.c index 836047c02d..53c6e66db2 100644 --- a/testsuite/gdk/memorytexture.c +++ b/testsuite/gdk/memorytexture.c @@ -31,6 +31,38 @@ struct _TextureBuilder gsize offset; }; +static inline guint +as_uint (const float x) +{ + return *(guint*)&x; +} + +static inline float +as_float (const guint x) +{ + return *(float*)&x; +} + +// IEEE-754 16-bit floating-point format (without infinity): 1-5-10 +// +static inline float +half_to_float (const guint16 x) +{ + const guint e = (x&0x7C00)>>10; // exponent + const guint m = (x&0x03FF)<<13; // mantissa + const guint v = as_uint((float)m)>>23; + return as_float((x&0x8000)<<16 | (e!=0)*((e+112)<<23|m) | ((e==0)&(m!=0))*((v-37)<<23|((m<<(150-v))&0x007FE000))); +} + +static inline guint16 +float_to_half (const float x) +{ + const guint b = *(guint*)&x+0x00001000; // round-to-nearest-even + const guint e = (b&0x7F800000)>>23; // exponent + const guint m = b&0x007FFFFF; // mantissa + return (b&0x80000000)>>16 | (e>112)*((((e-112)<<10)&0x7C00)|m>>13) | ((e<113)&(e>101))*((((0x007FF000+m)>>(125-e))+1)>>1) | (e>143)*0x7FFF; // sign : normalized : denormalized : saturate +} + static gsize gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format) { @@ -74,36 +106,73 @@ gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format) } static gboolean -gdk_memory_format_has_alpha (GdkMemoryFormat format) +gdk_memory_format_pixel_equal (GdkMemoryFormat format, + gboolean accurate, + const guchar *pixel1, + const guchar *pixel2) { switch (format) { - case GDK_MEMORY_R8G8B8: - case GDK_MEMORY_B8G8R8: - case GDK_MEMORY_R16G16B16: - case GDK_MEMORY_R16G16B16_FLOAT: - case GDK_MEMORY_R32G32B32_FLOAT: - return FALSE; - case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED: case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED: case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED: + case GDK_MEMORY_R8G8B8: + case GDK_MEMORY_B8G8R8: case GDK_MEMORY_B8G8R8A8: case GDK_MEMORY_A8R8G8B8: case GDK_MEMORY_R8G8B8A8: case GDK_MEMORY_A8B8G8R8: - case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: + return memcmp (pixel1, pixel2, gdk_memory_format_bytes_per_pixel (format)) == 0; + + case GDK_MEMORY_R16G16B16: case GDK_MEMORY_R16G16B16A16: - case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: + case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: + { + const guint16 *u1 = (const guint16 *) pixel1; + const guint16 *u2 = (const guint16 *) pixel2; + guint i; + for (i = 0; i < gdk_memory_format_bytes_per_pixel (format) / sizeof (guint16); i++) + { + if (!G_APPROX_VALUE (u1[i], u2[i], accurate ? 1 : 256)) + return FALSE; + } + } + return TRUE; + + case GDK_MEMORY_R16G16B16_FLOAT: case GDK_MEMORY_R16G16B16A16_FLOAT: - case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: + case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: + { + guint i; + for (i = 0; i < gdk_memory_format_bytes_per_pixel (format) / sizeof (guint16); i++) + { + float f1 = half_to_float (((guint16 *) pixel1)[i]); + float f2 = half_to_float (((guint16 *) pixel2)[i]); + if (!G_APPROX_VALUE (f1, f2, accurate ? 1./65535 : 1./255)) + return FALSE; + } + } + return TRUE; + + case GDK_MEMORY_R32G32B32_FLOAT: case GDK_MEMORY_R32G32B32A32_FLOAT: + case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: + { + const float *f1 = (const float *) pixel1; + const float *f2 = (const float *) pixel2; + guint i; + for (i = 0; i < gdk_memory_format_bytes_per_pixel (format) / sizeof (float); i++) + { + if (!G_APPROX_VALUE (f1[i], f2[i], accurate ? 1./65535 : 1./255)) + return FALSE; + } + } return TRUE; case GDK_MEMORY_N_FORMATS: default: g_assert_not_reached (); - return TRUE; + return FALSE; } } @@ -198,15 +267,6 @@ set_pixel_u8 (guchar *data, } } -static inline guint16 -float_to_half (const float x) -{ - const guint b = *(guint*)&x+0x00001000; // round-to-nearest-even - const guint e = (b&0x7F800000)>>23; // exponent - const guint m = b&0x007FFFFF; // mantissa - return (b&0x80000000)>>16 | (e>112)*((((e-112)<<10)&0x7C00)|m>>13) | ((e<113)&(e>101))*((((0x007FF000+m)>>(125-e))+1)>>1) | (e>143)*0x7FFF; // sign : normalized : denormalized : saturate -} - static void texture_builder_set_pixel (TextureBuilder *builder, int x, @@ -362,46 +422,60 @@ texture_builder_fill (TextureBuilder *builder, const GdkRGBA *color) { int x, y; - for (y = 0; y < builder->height; y++) for (x = 0; x < builder->width; x++) texture_builder_set_pixel (builder, x, y, color); } static void -compare_textures (GdkTexture *expected, - GdkTexture *test, - gboolean has_alpha) +compare_textures (GdkTexture *texture1, + GdkTexture *texture2, + gboolean accurate_compare) { - guint32 *expected_data, *test_data; - int width, height; - int x, y; - - g_assert_cmpint (gdk_texture_get_width (expected), ==, gdk_texture_get_width (test)); - g_assert_cmpint (gdk_texture_get_height (expected), ==, gdk_texture_get_height (test)); - - width = gdk_texture_get_width (expected); - height = gdk_texture_get_height (expected); - - expected_data = g_new (guint32, width * height); - gdk_texture_download (expected, (guchar *) expected_data, width * 4); - - test_data = g_new (guint32, width * height); - gdk_texture_download (test, (guchar *) test_data, width * 4); + GdkTextureDownloader *downloader1, *downloader2; + GBytes *bytes1, *bytes2; + gsize stride1, stride2, bpp; + const guchar *data1, *data2; + int width, height, x, y; + GdkMemoryFormat format; + g_assert_cmpint (gdk_texture_get_width (texture1), ==, gdk_texture_get_width (texture2)); + g_assert_cmpint (gdk_texture_get_height (texture1), ==, gdk_texture_get_height (texture2)); + g_assert_cmpint (gdk_texture_get_format (texture1), ==, gdk_texture_get_format (texture2)); + + format = gdk_texture_get_format (texture1); + bpp = gdk_memory_format_bytes_per_pixel (format); + width = gdk_texture_get_width (texture1); + height = gdk_texture_get_height (texture1); + + downloader1 = gdk_texture_downloader_new (texture1); + gdk_texture_downloader_set_format (downloader1, format); + bytes1 = gdk_texture_downloader_download_bytes (downloader1, &stride1); + g_assert_cmpint (stride1, >=, bpp * width); + g_assert_nonnull (bytes1); + gdk_texture_downloader_free (downloader1); + + downloader2 = gdk_texture_downloader_new (texture2); + gdk_texture_downloader_set_format (downloader2, format); + bytes2 = gdk_texture_downloader_download_bytes (downloader2, &stride2); + g_assert_cmpint (stride2, >=, bpp * width); + g_assert_nonnull (bytes2); + gdk_texture_downloader_free (downloader2); + + data1 = g_bytes_get_data (bytes1, NULL); + data2 = g_bytes_get_data (bytes2, NULL); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - if (has_alpha) - g_assert_cmphex (expected_data[y * width + x], ==, test_data[y * width + x]); - else - g_assert_cmphex (expected_data[y * width + x] | 0xFF000000, ==, test_data[y * width + x]); + g_assert_true (gdk_memory_format_pixel_equal (format, accurate_compare, data1 + bpp * x, data2 + bpp * x)); } + data1 += stride1; + data2 += stride2; } - g_free (expected_data); - g_free (test_data); + g_bytes_unref (bytes2); + g_bytes_unref (bytes1); } static GdkTexture * @@ -527,6 +601,56 @@ create_texture (GdkMemoryFormat format, return texture; } +static gboolean +texture_method_is_accurate (TextureMethod method) +{ + switch (method) + { + case TEXTURE_METHOD_LOCAL: + case TEXTURE_METHOD_TIFF: + return TRUE; + + case TEXTURE_METHOD_GL: + case TEXTURE_METHOD_GL_RELEASED: + case TEXTURE_METHOD_PNG: + case TEXTURE_METHOD_PNG_PIXBUF: + case TEXTURE_METHOD_TIFF_PIXBUF: + return FALSE; + + case N_TEXTURE_METHODS: + default: + g_assert_not_reached (); + return FALSE; + } +} + +static GdkTexture * +ensure_texture_format (GdkTexture *texture, + GdkMemoryFormat format) +{ + GdkTextureDownloader *downloader; + GdkTexture *result; + GBytes *bytes; + gsize stride; + + if (gdk_texture_get_format (texture) == format) + return texture; + + downloader = gdk_texture_downloader_new (texture); + gdk_texture_downloader_set_format (downloader, format); + bytes = gdk_texture_downloader_download_bytes (downloader, &stride); + gdk_texture_downloader_free (downloader); + + result = gdk_memory_texture_new (gdk_texture_get_width (texture), + gdk_texture_get_height (texture), + format, + bytes, + stride); + g_object_unref (texture); + + return result; +} + static void create_random_color (GdkRGBA *color) { @@ -553,10 +677,12 @@ test_download_1x1 (gconstpointer data) GdkRGBA color; create_random_color (&color); - expected = create_texture (GDK_MEMORY_DEFAULT, TEXTURE_METHOD_LOCAL, 1, 1, &color); + + expected = create_texture (format, TEXTURE_METHOD_LOCAL, 1, 1, &color); test = create_texture (format, method, 1, 1, &color); + test = ensure_texture_format (test, format); - compare_textures (expected, test, gdk_memory_format_has_alpha (format)); + compare_textures (expected, test, texture_method_is_accurate (method)); g_object_unref (expected); g_object_unref (test); @@ -579,10 +705,12 @@ test_download_4x4 (gconstpointer data) GdkRGBA color; create_random_color (&color); - expected = create_texture (GDK_MEMORY_DEFAULT, TEXTURE_METHOD_LOCAL, 4, 4, &color); + + expected = create_texture (format, TEXTURE_METHOD_LOCAL, 4, 4, &color); test = create_texture (format, method, 4, 4, &color); + test = ensure_texture_format (test, format); - compare_textures (expected, test, gdk_memory_format_has_alpha (format)); + compare_textures (expected, test, texture_method_is_accurate (method)); g_object_unref (expected); g_object_unref (test); @@ -602,10 +730,12 @@ test_download_192x192 (gconstpointer data) return; create_random_color (&color); - expected = create_texture (GDK_MEMORY_DEFAULT, TEXTURE_METHOD_LOCAL, 192, 192, &color); + + expected = create_texture (format, TEXTURE_METHOD_LOCAL, 192, 192, &color); test = create_texture (format, method, 192, 192, &color); + test = ensure_texture_format (test, format); - compare_textures (expected, test, gdk_memory_format_has_alpha (format)); + compare_textures (expected, test, texture_method_is_accurate (method)); g_object_unref (expected); g_object_unref (test); -- 2.30.2