summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/igt_frame.c135
-rw-r--r--lib/igt_frame.h2
2 files changed, 137 insertions, 0 deletions
diff --git a/lib/igt_frame.c b/lib/igt_frame.c
index 6984c02e9..45523a79f 100644
--- a/lib/igt_frame.c
+++ b/lib/igt_frame.c
@@ -267,3 +267,138 @@ complete:
return match;
}
+
+#define XR24_COLOR_VALUE(data, stride, x, y, c) \
+ *((uint8_t *)(data) + (y) * (stride) + 4 * (x) + (c))
+
+/**
+ * igt_check_checkerboard_frame_match:
+ * @reference: The reference cairo surface
+ * @capture: The captured cairo surface
+ *
+ * Checks that the reference frame matches the captured frame using a
+ * method designed for checkerboard patterns. These patterns are made of
+ * consecutive rectangular shapes with alternating solid colors.
+ *
+ * The intent of this method is to cover cases where the captured result is
+ * not pixel-perfect due to features such as scaling or YUV conversion and
+ * subsampling. Such effects are mostly noticeable on the edges of the
+ * patterns, so they are detected and excluded from the comparison.
+ *
+ * The algorithm works with two major steps. First, the edges of the reference
+ * pattern are detected on the x and y axis independently. The detection is done
+ * by calculating an absolute difference with a span of a few pixels before and
+ * after the current position on the given axis, accumulated for each color
+ * component. If the sum is above a given threshold on one of the axis, the
+ * position is marked as an edge. In the second step, the pixel values are
+ * compared per-component, excluding positions that were marked as edges or
+ * that are at a transition between an edge and a non-edge. An error threshold
+ * (for each individual color component) is used to mark the position as
+ * erroneous or not. The ratio of erroneous pixels over compared pixels (that
+ * does not count excluded pixels) is then calculated and compared to the error
+ * rate threshold to determine whether the frames match or not.
+ *
+ * Returns: a boolean indicating whether the frames match
+ */
+bool igt_check_checkerboard_frame_match(cairo_surface_t *reference,
+ cairo_surface_t *capture)
+{
+ unsigned int width, height, ref_stride, cap_stride;
+ void *ref_data, *cap_data;
+ unsigned char *edges_map;
+ unsigned int x, y, c;
+ unsigned int errors = 0, pixels = 0;
+ unsigned int edge_threshold = 100;
+ unsigned int color_error_threshold = 24;
+ double error_rate_threshold = 0.01;
+ double error_rate;
+ unsigned int span = 2;
+ bool match = false;
+
+ width = cairo_image_surface_get_width(reference);
+ height = cairo_image_surface_get_height(reference);
+
+ ref_stride = cairo_image_surface_get_stride(reference);
+ ref_data = cairo_image_surface_get_data(reference);
+ igt_assert(ref_data);
+
+ cap_stride = cairo_image_surface_get_stride(capture);
+ cap_data = cairo_image_surface_get_data(capture);
+ igt_assert(cap_data);
+
+ edges_map = calloc(1, width * height);
+ igt_assert(edges_map);
+
+ /* First pass to detect the pattern edges. */
+ for (y = 0; y < height; y++) {
+ if (y < span || y > (height - span - 1))
+ continue;
+
+ for (x = 0; x < width; x++) {
+ unsigned int xdiff = 0, ydiff = 0;
+
+ if (x < span || x > (width - span - 1))
+ continue;
+
+ for (c = 0; c < 3; c++) {
+ xdiff += abs(XR24_COLOR_VALUE(ref_data, ref_stride, x + span, y, c) -
+ XR24_COLOR_VALUE(ref_data, ref_stride, x - span, y, c));
+ ydiff += abs(XR24_COLOR_VALUE(ref_data, ref_stride, x, y + span, c) -
+ XR24_COLOR_VALUE(ref_data, ref_stride, x, y - span, c));
+ }
+
+ edges_map[y * width + x] = (xdiff > edge_threshold ||
+ ydiff > edge_threshold);
+ }
+ }
+
+ /* Second pass to detect errors. */
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ bool error = false;
+
+ if (edges_map[y * width + x])
+ continue;
+
+ for (c = 0; c < 3; c++) {
+ unsigned int diff;
+
+ /* Compare the reference and capture values. */
+ diff = abs(XR24_COLOR_VALUE(ref_data, ref_stride, x, y, c) -
+ XR24_COLOR_VALUE(cap_data, cap_stride, x, y, c));
+
+ if (diff > color_error_threshold)
+ error = true;
+ }
+
+ /* Allow error if coming on or off an edge (on x). */
+ if (error && x >= span && x <= (width - span - 1) &&
+ edges_map[y * width + (x - span)] !=
+ edges_map[y * width + (x + span)])
+ continue;
+
+ /* Allow error if coming on or off an edge (on y). */
+ if (error && y >= span && y <= (height - span - 1) &&
+ edges_map[(y - span) * width + x] !=
+ edges_map[(y + span) * width + x] && error)
+ continue;
+
+ if (error)
+ errors++;
+
+ pixels++;
+ }
+ }
+
+ free(edges_map);
+
+ error_rate = (double) errors / pixels;
+
+ if (error_rate < error_rate_threshold)
+ match = true;
+
+ igt_debug("Checkerboard pattern %s with error rate %f %%\n",
+ match ? "matched" : "not matched", error_rate * 100);
+
+ return match;
+}
diff --git a/lib/igt_frame.h b/lib/igt_frame.h
index 11f96cbea..f44f57d7c 100644
--- a/lib/igt_frame.h
+++ b/lib/igt_frame.h
@@ -38,5 +38,7 @@ void igt_write_compared_frames_to_png(cairo_surface_t *reference,
const char *capture_suffix);
bool igt_check_analog_frame_match(cairo_surface_t *reference,
cairo_surface_t *capture);
+bool igt_check_checkerboard_frame_match(cairo_surface_t *reference,
+ cairo_surface_t *capture);
#endif