diff --git a/testsuite/reftests/reftest-compare.c b/testsuite/reftests/reftest-compare.c index bb6c6ebc1a..3d5598b9f9 100644 --- a/testsuite/reftests/reftest-compare.c +++ b/testsuite/reftests/reftest-compare.c @@ -22,6 +22,55 @@ #include "reftest-compare.h" +static gboolean +memory_format_is_high_depth (GdkMemoryFormat format) +{ + switch (format) + { + case GDK_MEMORY_R8G8B8: + case GDK_MEMORY_B8G8R8: + case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED: + case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED: + case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED: + case GDK_MEMORY_A8B8G8R8_PREMULTIPLIED: + case GDK_MEMORY_B8G8R8A8: + case GDK_MEMORY_A8R8G8B8: + case GDK_MEMORY_R8G8B8A8: + case GDK_MEMORY_A8B8G8R8: + case GDK_MEMORY_B8G8R8X8: + case GDK_MEMORY_X8R8G8B8: + case GDK_MEMORY_R8G8B8X8: + case GDK_MEMORY_X8B8G8R8: + case GDK_MEMORY_G8: + case GDK_MEMORY_G8A8: + case GDK_MEMORY_G8A8_PREMULTIPLIED: + case GDK_MEMORY_A8: + return FALSE; + + case GDK_MEMORY_R16G16B16: + case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: + case GDK_MEMORY_R16G16B16A16: + case GDK_MEMORY_G16: + case GDK_MEMORY_G16A16: + case GDK_MEMORY_G16A16_PREMULTIPLIED: + case GDK_MEMORY_A16: + case GDK_MEMORY_R16G16B16_FLOAT: + case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: + case GDK_MEMORY_R16G16B16A16_FLOAT: + case GDK_MEMORY_A16_FLOAT: + case GDK_MEMORY_R32G32B32_FLOAT: + case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: + case GDK_MEMORY_R32G32B32A32_FLOAT: + case GDK_MEMORY_A32_FLOAT: + return TRUE; + + case GDK_MEMORY_N_FORMATS: + default: + g_assert_not_reached (); + return FALSE; + } +} + /* Compares two GDK_MEMORY_DEFAULT buffers, returning NULL if the * buffers are equal or a surface containing a diff between the two * surfaces. @@ -30,12 +79,12 @@ * Copyright © 2004 Richard D. Worth */ static GdkTexture * -buffer_diff_core (const guchar *buf_a, - int stride_a, - const guchar *buf_b, - int stride_b, - int width, - int height) +buffer_diff_u8 (const guchar *buf_a, + int stride_a, + const guchar *buf_b, + int stride_b, + int width, + int height) { int x, y; guchar *buf_diff = NULL; @@ -108,25 +157,138 @@ buffer_diff_core (const guchar *buf_a, return diff; } +/* Compares two FLOAT buffers, returning NULL if the + * buffers are equal or a surface containing a diff between the two + * surfaces. + */ +static GdkTexture * +buffer_diff_float (const guchar *buf_a, + int stride_a, + const guchar *buf_b, + int stride_b, + int width, + int height) +{ + int x, y; + guchar *buf_diff = NULL; + int stride_diff = 0; + GdkTexture *diff = NULL; + + for (y = 0; y < height; y++) + { + const float *row_a = (const float *) (buf_a + y * stride_a); + const float *row_b = (const float *) (buf_b + y * stride_b); + float *row = (float *) (buf_diff + y * stride_diff); + + for (x = 0; x < width; x++) + { + int channel; + + /* check if the pixels are the same */ + if (G_APPROX_VALUE (row_a[4 * x + 0], row_b[4 * x + 0], 1. / 255) && + G_APPROX_VALUE (row_a[4 * x + 1], row_b[4 * x + 1], 1. / 255) && + G_APPROX_VALUE (row_a[4 * x + 2], row_b[4 * x + 2], 1. / 255) && + G_APPROX_VALUE (row_a[4 * x + 3], row_b[4 * x + 3], 1. / 255)) + continue; + + /* even if they're not literally the same, fully-transparent + * pixels are effectively the same regardless of colour */ + if (G_APPROX_VALUE (row_a[4 * x + 3], 0.0, 1. / 255) && + G_APPROX_VALUE (row_b[4 * x + 3], 0.0, 1. / 255)) + continue; + + if (diff == NULL) + { + GBytes *bytes; + + stride_diff = 4 * width * sizeof (float); + buf_diff = g_malloc0_n (stride_diff, height); + bytes = g_bytes_new_take (buf_diff, stride_diff * height); + diff = gdk_memory_texture_new (width, height, + GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED, + bytes, + stride_diff); + row = (float *) (buf_diff + y * stride_diff); + } + + /* calculate a difference value for all 4 channels */ + for (channel = 0; channel < 4; channel++) + { + float value_a = row_a[4 * x + channel]; + float value_b = row_b[4 * x + channel];\ + float channel_diff; + + channel_diff = ABS (value_a - value_b); + channel_diff *= 4; /* emphasize */ + if (channel_diff) + channel_diff += 0.5; /* make sure it's visible */ + if (channel_diff > 1.0) + channel_diff = 1.0; + row[4 * x + channel] = channel_diff; + } + + if (row[4 * x + 0] < 0.5 && + row[4 * x + 1] < 0.5 && + row[4 * x + 2] < 0.5) + { + /* alpha only difference, convert to luminance */ + row[4 * x + 0] = row[4 * x + 3]; + row[4 * x + 1] = row[4 * x + 3]; + row[4 * x + 2] = row[4 * x + 3]; + } + /* make the pixel fully opaque */ + row[4 * x + 3] = 1.0; + } + } + + return diff; +} + GdkTexture * reftest_compare_textures (GdkTexture *texture1, GdkTexture *texture2) { - int w, h; + int w, h, stride; guchar *data1, *data2; + GdkTextureDownloader *downloader; GdkTexture *diff; + gboolean high_depth; w = MAX (gdk_texture_get_width (texture1), gdk_texture_get_width (texture2)); h = MAX (gdk_texture_get_height (texture1), gdk_texture_get_height (texture2)); - data1 = g_malloc_n (w * 4, h); - gdk_texture_download (texture1, data1, w * 4); - data2 = g_malloc_n (w * 4, h); - gdk_texture_download (texture2, data2, w * 4); + downloader = gdk_texture_downloader_new (texture1); + high_depth = memory_format_is_high_depth (gdk_texture_get_format (texture1)) || + memory_format_is_high_depth (gdk_texture_get_format (texture1)); + if (high_depth) + { + gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED); + stride = w * 4 * sizeof (float); + } + else + { + gdk_texture_downloader_set_format (downloader, GDK_MEMORY_DEFAULT); + stride = w * 4 * sizeof (float); + } - diff = buffer_diff_core (data1, w * 4, - data2, w * 4, - w, h); + data1 = g_malloc_n (stride, h); + gdk_texture_downloader_download_into (downloader, data1, stride); + data2 = g_malloc_n (stride, h); + gdk_texture_downloader_set_texture (downloader, texture2); + gdk_texture_downloader_download_into (downloader, data2, stride); + + if (high_depth) + { + diff = buffer_diff_float (data1, stride, + data2, stride, + w, h); + } + else + { + diff = buffer_diff_u8 (data1, stride, + data2, stride, + w, h); + } g_free (data1); g_free (data2);