From e70aa1fde23fed697ebbdec4f72e79afc885917f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 16 Apr 2021 17:42:40 +0300 Subject: [PATCH] tests/alpha-blending: reference blending Instead of checking just the monotonicity of the blending results, this changes the alpha-blending test to compute the reference blend result itself and then comparing to the compositor result. This way we can be sure that the compositor implements the exact correct formula and not something that just looks nice, as verifying the reference images are actually correct is hard. The reference image is renamed to follow the fact that this is not primarily a monotonicity test anymore. The reference image is also redundant, but I think it has documentary value. The #if 0'd block of code was very useful in figuring out blending errors in a future test case, so it is included here. I have a feeling we are going to need it again. Signed-off-by: Pekka Paalanen --- tests/alpha-blending-test.c | 121 +++++++++++++++++- tests/meson.build | 5 +- ...nd_monotonic-00.png => alpha_blend-00.png} | Bin 3 files changed, 118 insertions(+), 8 deletions(-) rename tests/reference/{alpha_blend_monotonic-00.png => alpha_blend-00.png} (100%) diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index 6d525d19..aaf77e54 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -25,9 +25,7 @@ #include "config.h" -#include -#include -#include +#include #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" @@ -125,6 +123,105 @@ fill_alpha_pattern(struct buffer *buf) } } +struct color_float { + float r, g, b, a; +}; + +static struct color_float +a8r8g8b8_to_float(uint32_t v) +{ + struct color_float cf; + + cf.a = ((v >> 24) & 0xff) / 255.f; + cf.r = ((v >> 16) & 0xff) / 255.f; + cf.g = ((v >> 8) & 0xff) / 255.f; + cf.b = ((v >> 0) & 0xff) / 255.f; + + return cf; +} + +static void +unpremult_float(struct color_float *cf) +{ + if (cf->a == 0.0f) { + cf->r = 0.0f; + cf->g = 0.0f; + cf->b = 0.0f; + } else { + cf->r /= cf->a; + cf->g /= cf->a; + cf->b /= cf->a; + } +} + +static bool +compare_float(float ref, float dst, int x, const char *chan, float *max_diff) +{ +#if 0 + /* + * This file can be loaded in Octave for visualization. + * + * S = load('compare_float_dump.txt'); + * + * rvec = S(S(:,1)==114, 2:3); + * gvec = S(S(:,1)==103, 2:3); + * bvec = S(S(:,1)==98, 2:3); + * + * figure + * subplot(3, 1, 1); + * plot(rvec(:,1), rvec(:,2) .* 255, 'r'); + * subplot(3, 1, 2); + * plot(gvec(:,1), gvec(:,2) .* 255, 'g'); + * subplot(3, 1, 3); + * plot(bvec(:,1), bvec(:,2) .* 255, 'b'); + */ + static FILE *fp = NULL; + + if (!fp) + fp = fopen("compare_float_dump.txt", "w"); + fprintf(fp, "%d %d %f\n", chan[0], x, dst - ref); + fflush(fp); +#endif + + float diff = fabsf(ref - dst); + + if (diff > *max_diff) + *max_diff = diff; + + if (diff < 0.5f / 255.f) + return true; + + testlog("x=%d %s: ref %f != dst %f, delta %f\n", + x, chan, ref, dst, dst - ref); + + return false; +} + +static bool +verify_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, + int x, struct color_float *max_diff) +{ + struct color_float bg = a8r8g8b8_to_float(bg32); + struct color_float fg = a8r8g8b8_to_float(fg32); + struct color_float dst = a8r8g8b8_to_float(dst32); + struct color_float ref; + bool ok = true; + + unpremult_float(&bg); + unpremult_float(&fg); + unpremult_float(&dst); + + ref.r = (1.0f - fg.a) * bg.r + fg.a * fg.r; + ref.g = (1.0f - fg.a) * bg.g + fg.a * fg.g; + ref.b = (1.0f - fg.a) * bg.b + fg.a * fg.b; + + ok = compare_float(ref.r, dst.r, x, "r", &max_diff->r) && ok; + ok = compare_float(ref.g, dst.g, x, "g", &max_diff->g) && ok; + ok = compare_float(ref.b, dst.b, x, "b", &max_diff->b) && ok; + + return ok; +} + static uint8_t red(uint32_t v) { @@ -171,17 +268,27 @@ get_middle_row(struct buffer *buf) } static bool -check_blend_pattern(struct buffer *shot) +check_blend_pattern(struct buffer *bg, struct buffer *fg, struct buffer *shot) { + uint32_t *bg_row = get_middle_row(bg); + uint32_t *fg_row = get_middle_row(fg); uint32_t *shot_row = get_middle_row(shot); + struct color_float max_diff = { 0.0f, 0.0f, 0.0f, 0.0f }; bool ret = true; int x; for (x = 0; x < BLOCK_WIDTH * ALPHA_STEPS - 1; x++) { if (!pixels_monotonic(shot_row, x)) ret = false; + + if (!verify_sRGB_blend_a8r8g8b8(bg_row[x], fg_row[x], + shot_row[x], x, &max_diff)) + ret = false; } + testlog("%s max diff: r=%f, g=%f, b=%f\n", + __func__, max_diff.r, max_diff.g, max_diff.b); + return ret; } @@ -203,7 +310,7 @@ check_blend_pattern(struct buffer *shot) * - green is not monotonic * - blue goes from 0.0 to 1.0, monotonic */ -TEST(alpha_blend_monotonic) +TEST(alpha_blend) { const int width = BLOCK_WIDTH * ALPHA_STEPS; const int height = BLOCK_WIDTH; @@ -254,10 +361,10 @@ TEST(alpha_blend_monotonic) shot = capture_screenshot_of_output(client); assert(shot); - match = verify_image(shot, "alpha_blend_monotonic", 0, NULL, 0); + match = verify_image(shot, "alpha_blend", 0, NULL, 0); assert(match); - assert(check_blend_pattern(shot)); + assert(check_blend_pattern(bg, fg, shot)); buffer_destroy(shot); wl_subsurface_destroy(sub); diff --git a/tests/meson.build b/tests/meson.build index 4f7fcec2..fe81cecc 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -118,7 +118,10 @@ dep_zucmain = declare_dependency( ) tests = [ - { 'name': 'alpha-blending', }, + { + 'name': 'alpha-blending', + 'dep_objs': dep_libm, + }, { 'name': 'bad-buffer', }, { 'name': 'buffer-transforms', }, { 'name': 'color-manager', }, diff --git a/tests/reference/alpha_blend_monotonic-00.png b/tests/reference/alpha_blend-00.png similarity index 100% rename from tests/reference/alpha_blend_monotonic-00.png rename to tests/reference/alpha_blend-00.png