1*d83cc019SAndroid Build Coastguard Worker /*
2*d83cc019SAndroid Build Coastguard Worker * Copyright © 2017 Intel Corporation
3*d83cc019SAndroid Build Coastguard Worker *
4*d83cc019SAndroid Build Coastguard Worker * Permission is hereby granted, free of charge, to any person obtaining a
5*d83cc019SAndroid Build Coastguard Worker * copy of this software and associated documentation files (the "Software"),
6*d83cc019SAndroid Build Coastguard Worker * to deal in the Software without restriction, including without limitation
7*d83cc019SAndroid Build Coastguard Worker * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*d83cc019SAndroid Build Coastguard Worker * and/or sell copies of the Software, and to permit persons to whom the
9*d83cc019SAndroid Build Coastguard Worker * Software is furnished to do so, subject to the following conditions:
10*d83cc019SAndroid Build Coastguard Worker *
11*d83cc019SAndroid Build Coastguard Worker * The above copyright notice and this permission notice (including the next
12*d83cc019SAndroid Build Coastguard Worker * paragraph) shall be included in all copies or substantial portions of the
13*d83cc019SAndroid Build Coastguard Worker * Software.
14*d83cc019SAndroid Build Coastguard Worker *
15*d83cc019SAndroid Build Coastguard Worker * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*d83cc019SAndroid Build Coastguard Worker * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*d83cc019SAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18*d83cc019SAndroid Build Coastguard Worker * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*d83cc019SAndroid Build Coastguard Worker * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20*d83cc019SAndroid Build Coastguard Worker * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21*d83cc019SAndroid Build Coastguard Worker * IN THE SOFTWARE.
22*d83cc019SAndroid Build Coastguard Worker *
23*d83cc019SAndroid Build Coastguard Worker * Authors:
24*d83cc019SAndroid Build Coastguard Worker * Paul Kocialkowski <[email protected]>
25*d83cc019SAndroid Build Coastguard Worker */
26*d83cc019SAndroid Build Coastguard Worker
27*d83cc019SAndroid Build Coastguard Worker #include "config.h"
28*d83cc019SAndroid Build Coastguard Worker
29*d83cc019SAndroid Build Coastguard Worker #include <fcntl.h>
30*d83cc019SAndroid Build Coastguard Worker #include <pixman.h>
31*d83cc019SAndroid Build Coastguard Worker #include <cairo.h>
32*d83cc019SAndroid Build Coastguard Worker #include <gsl/gsl_statistics_double.h>
33*d83cc019SAndroid Build Coastguard Worker #include <gsl/gsl_fit.h>
34*d83cc019SAndroid Build Coastguard Worker
35*d83cc019SAndroid Build Coastguard Worker #include "igt_frame.h"
36*d83cc019SAndroid Build Coastguard Worker #include "igt_core.h"
37*d83cc019SAndroid Build Coastguard Worker
38*d83cc019SAndroid Build Coastguard Worker /**
39*d83cc019SAndroid Build Coastguard Worker * SECTION:igt_frame
40*d83cc019SAndroid Build Coastguard Worker * @short_description: Library for frame-related tests
41*d83cc019SAndroid Build Coastguard Worker * @title: Frame
42*d83cc019SAndroid Build Coastguard Worker * @include: igt_frame.h
43*d83cc019SAndroid Build Coastguard Worker *
44*d83cc019SAndroid Build Coastguard Worker * This library contains helpers for frame-related tests. This includes common
45*d83cc019SAndroid Build Coastguard Worker * frame dumping as well as frame comparison helpers.
46*d83cc019SAndroid Build Coastguard Worker */
47*d83cc019SAndroid Build Coastguard Worker
48*d83cc019SAndroid Build Coastguard Worker /**
49*d83cc019SAndroid Build Coastguard Worker * igt_frame_dump_is_enabled:
50*d83cc019SAndroid Build Coastguard Worker *
51*d83cc019SAndroid Build Coastguard Worker * Get whether frame dumping is enabled.
52*d83cc019SAndroid Build Coastguard Worker *
53*d83cc019SAndroid Build Coastguard Worker * Returns: A boolean indicating whether frame dumping is enabled
54*d83cc019SAndroid Build Coastguard Worker */
igt_frame_dump_is_enabled(void)55*d83cc019SAndroid Build Coastguard Worker bool igt_frame_dump_is_enabled(void)
56*d83cc019SAndroid Build Coastguard Worker {
57*d83cc019SAndroid Build Coastguard Worker return igt_frame_dump_path != NULL;
58*d83cc019SAndroid Build Coastguard Worker }
59*d83cc019SAndroid Build Coastguard Worker
igt_write_frame_to_png(cairo_surface_t * surface,int fd,const char * qualifier,const char * suffix)60*d83cc019SAndroid Build Coastguard Worker static void igt_write_frame_to_png(cairo_surface_t *surface, int fd,
61*d83cc019SAndroid Build Coastguard Worker const char *qualifier, const char *suffix)
62*d83cc019SAndroid Build Coastguard Worker {
63*d83cc019SAndroid Build Coastguard Worker char path[PATH_MAX];
64*d83cc019SAndroid Build Coastguard Worker const char *test_name;
65*d83cc019SAndroid Build Coastguard Worker const char *subtest_name;
66*d83cc019SAndroid Build Coastguard Worker cairo_status_t status;
67*d83cc019SAndroid Build Coastguard Worker int index;
68*d83cc019SAndroid Build Coastguard Worker
69*d83cc019SAndroid Build Coastguard Worker test_name = igt_test_name();
70*d83cc019SAndroid Build Coastguard Worker subtest_name = igt_subtest_name();
71*d83cc019SAndroid Build Coastguard Worker
72*d83cc019SAndroid Build Coastguard Worker if (suffix)
73*d83cc019SAndroid Build Coastguard Worker snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s-%s.png",
74*d83cc019SAndroid Build Coastguard Worker igt_frame_dump_path, test_name, subtest_name, qualifier,
75*d83cc019SAndroid Build Coastguard Worker suffix);
76*d83cc019SAndroid Build Coastguard Worker else
77*d83cc019SAndroid Build Coastguard Worker snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s.png",
78*d83cc019SAndroid Build Coastguard Worker igt_frame_dump_path, test_name, subtest_name, qualifier);
79*d83cc019SAndroid Build Coastguard Worker
80*d83cc019SAndroid Build Coastguard Worker igt_debug("Dumping %s frame to %s...\n", qualifier, path);
81*d83cc019SAndroid Build Coastguard Worker
82*d83cc019SAndroid Build Coastguard Worker status = cairo_surface_write_to_png(surface, path);
83*d83cc019SAndroid Build Coastguard Worker
84*d83cc019SAndroid Build Coastguard Worker igt_assert_eq(status, CAIRO_STATUS_SUCCESS);
85*d83cc019SAndroid Build Coastguard Worker
86*d83cc019SAndroid Build Coastguard Worker index = strlen(path);
87*d83cc019SAndroid Build Coastguard Worker
88*d83cc019SAndroid Build Coastguard Worker if (fd >= 0 && index < (PATH_MAX - 1)) {
89*d83cc019SAndroid Build Coastguard Worker path[index++] = '\n';
90*d83cc019SAndroid Build Coastguard Worker path[index] = '\0';
91*d83cc019SAndroid Build Coastguard Worker
92*d83cc019SAndroid Build Coastguard Worker write(fd, path, strlen(path));
93*d83cc019SAndroid Build Coastguard Worker }
94*d83cc019SAndroid Build Coastguard Worker }
95*d83cc019SAndroid Build Coastguard Worker
96*d83cc019SAndroid Build Coastguard Worker /**
97*d83cc019SAndroid Build Coastguard Worker * igt_write_compared_frames_to_png:
98*d83cc019SAndroid Build Coastguard Worker * @reference: The reference cairo surface
99*d83cc019SAndroid Build Coastguard Worker * @capture: The captured cairo surface
100*d83cc019SAndroid Build Coastguard Worker * @reference_suffix: The suffix to give to the reference png file
101*d83cc019SAndroid Build Coastguard Worker * @capture_suffix: The suffix to give to the capture png file
102*d83cc019SAndroid Build Coastguard Worker *
103*d83cc019SAndroid Build Coastguard Worker * Write previously compared frames to png files.
104*d83cc019SAndroid Build Coastguard Worker */
igt_write_compared_frames_to_png(cairo_surface_t * reference,cairo_surface_t * capture,const char * reference_suffix,const char * capture_suffix)105*d83cc019SAndroid Build Coastguard Worker void igt_write_compared_frames_to_png(cairo_surface_t *reference,
106*d83cc019SAndroid Build Coastguard Worker cairo_surface_t *capture,
107*d83cc019SAndroid Build Coastguard Worker const char *reference_suffix,
108*d83cc019SAndroid Build Coastguard Worker const char *capture_suffix)
109*d83cc019SAndroid Build Coastguard Worker {
110*d83cc019SAndroid Build Coastguard Worker char *id;
111*d83cc019SAndroid Build Coastguard Worker const char *test_name;
112*d83cc019SAndroid Build Coastguard Worker const char *subtest_name;
113*d83cc019SAndroid Build Coastguard Worker char path[PATH_MAX];
114*d83cc019SAndroid Build Coastguard Worker int fd = -1;
115*d83cc019SAndroid Build Coastguard Worker
116*d83cc019SAndroid Build Coastguard Worker if (!igt_frame_dump_is_enabled())
117*d83cc019SAndroid Build Coastguard Worker return;
118*d83cc019SAndroid Build Coastguard Worker
119*d83cc019SAndroid Build Coastguard Worker id = getenv("IGT_FRAME_DUMP_ID");
120*d83cc019SAndroid Build Coastguard Worker
121*d83cc019SAndroid Build Coastguard Worker test_name = igt_test_name();
122*d83cc019SAndroid Build Coastguard Worker subtest_name = igt_subtest_name();
123*d83cc019SAndroid Build Coastguard Worker
124*d83cc019SAndroid Build Coastguard Worker if (id)
125*d83cc019SAndroid Build Coastguard Worker snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s.txt",
126*d83cc019SAndroid Build Coastguard Worker igt_frame_dump_path, test_name, subtest_name, id);
127*d83cc019SAndroid Build Coastguard Worker else
128*d83cc019SAndroid Build Coastguard Worker snprintf(path, PATH_MAX, "%s/frame-%s-%s.txt",
129*d83cc019SAndroid Build Coastguard Worker igt_frame_dump_path, test_name, subtest_name);
130*d83cc019SAndroid Build Coastguard Worker
131*d83cc019SAndroid Build Coastguard Worker fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
132*d83cc019SAndroid Build Coastguard Worker igt_assert(fd >= 0);
133*d83cc019SAndroid Build Coastguard Worker
134*d83cc019SAndroid Build Coastguard Worker igt_debug("Writing dump report to %s...\n", path);
135*d83cc019SAndroid Build Coastguard Worker
136*d83cc019SAndroid Build Coastguard Worker igt_write_frame_to_png(reference, fd, "reference", reference_suffix);
137*d83cc019SAndroid Build Coastguard Worker igt_write_frame_to_png(capture, fd, "capture", capture_suffix);
138*d83cc019SAndroid Build Coastguard Worker
139*d83cc019SAndroid Build Coastguard Worker close(fd);
140*d83cc019SAndroid Build Coastguard Worker }
141*d83cc019SAndroid Build Coastguard Worker
142*d83cc019SAndroid Build Coastguard Worker /**
143*d83cc019SAndroid Build Coastguard Worker * igt_check_analog_frame_match:
144*d83cc019SAndroid Build Coastguard Worker * @reference: The reference cairo surface
145*d83cc019SAndroid Build Coastguard Worker * @capture: The captured cairo surface
146*d83cc019SAndroid Build Coastguard Worker *
147*d83cc019SAndroid Build Coastguard Worker * Checks that the analog image contained in the chamelium frame dump matches
148*d83cc019SAndroid Build Coastguard Worker * the given framebuffer.
149*d83cc019SAndroid Build Coastguard Worker *
150*d83cc019SAndroid Build Coastguard Worker * In order to determine whether the frame matches the reference, the following
151*d83cc019SAndroid Build Coastguard Worker * reasoning is implemented:
152*d83cc019SAndroid Build Coastguard Worker * 1. The absolute error for each color value of the reference is collected.
153*d83cc019SAndroid Build Coastguard Worker * 2. The average absolute error is calculated for each color value of the
154*d83cc019SAndroid Build Coastguard Worker * reference and must not go above 60 (23.5 % of the total range).
155*d83cc019SAndroid Build Coastguard Worker * 3. A linear fit for the average absolute error from the pixel value is
156*d83cc019SAndroid Build Coastguard Worker * calculated, as a DAC-ADC chain is expected to have a linear error curve.
157*d83cc019SAndroid Build Coastguard Worker * 4. The linear fit is correlated with the actual average absolute error for
158*d83cc019SAndroid Build Coastguard Worker * the frame and the correlation coefficient is checked to be > 0.985,
159*d83cc019SAndroid Build Coastguard Worker * indicating a match with the expected error trend.
160*d83cc019SAndroid Build Coastguard Worker *
161*d83cc019SAndroid Build Coastguard Worker * Most errors (e.g. due to scaling, rotation, color space, etc) can be
162*d83cc019SAndroid Build Coastguard Worker * reliably detected this way, with a minimized number of false-positives.
163*d83cc019SAndroid Build Coastguard Worker * However, the brightest values (250 and up) are ignored as the error trend
164*d83cc019SAndroid Build Coastguard Worker * is often not linear there in practice due to clamping.
165*d83cc019SAndroid Build Coastguard Worker *
166*d83cc019SAndroid Build Coastguard Worker * Returns: a boolean indicating whether the frames match
167*d83cc019SAndroid Build Coastguard Worker */
168*d83cc019SAndroid Build Coastguard Worker
igt_check_analog_frame_match(cairo_surface_t * reference,cairo_surface_t * capture)169*d83cc019SAndroid Build Coastguard Worker bool igt_check_analog_frame_match(cairo_surface_t *reference,
170*d83cc019SAndroid Build Coastguard Worker cairo_surface_t *capture)
171*d83cc019SAndroid Build Coastguard Worker {
172*d83cc019SAndroid Build Coastguard Worker pixman_image_t *reference_src, *capture_src;
173*d83cc019SAndroid Build Coastguard Worker int w, h;
174*d83cc019SAndroid Build Coastguard Worker int error_count[3][256][2] = { 0 };
175*d83cc019SAndroid Build Coastguard Worker double error_average[4][250];
176*d83cc019SAndroid Build Coastguard Worker double error_trend[250];
177*d83cc019SAndroid Build Coastguard Worker double c0, c1, cov00, cov01, cov11, sumsq;
178*d83cc019SAndroid Build Coastguard Worker double correlation;
179*d83cc019SAndroid Build Coastguard Worker unsigned char *reference_pixels, *capture_pixels;
180*d83cc019SAndroid Build Coastguard Worker unsigned char *p;
181*d83cc019SAndroid Build Coastguard Worker unsigned char *q;
182*d83cc019SAndroid Build Coastguard Worker bool match = true;
183*d83cc019SAndroid Build Coastguard Worker int diff;
184*d83cc019SAndroid Build Coastguard Worker int x, y;
185*d83cc019SAndroid Build Coastguard Worker int i, j;
186*d83cc019SAndroid Build Coastguard Worker
187*d83cc019SAndroid Build Coastguard Worker w = cairo_image_surface_get_width(reference);
188*d83cc019SAndroid Build Coastguard Worker h = cairo_image_surface_get_height(reference);
189*d83cc019SAndroid Build Coastguard Worker
190*d83cc019SAndroid Build Coastguard Worker reference_src = pixman_image_create_bits(
191*d83cc019SAndroid Build Coastguard Worker PIXMAN_x8r8g8b8, w, h,
192*d83cc019SAndroid Build Coastguard Worker (void*)cairo_image_surface_get_data(reference),
193*d83cc019SAndroid Build Coastguard Worker cairo_image_surface_get_stride(reference));
194*d83cc019SAndroid Build Coastguard Worker reference_pixels = (unsigned char *) pixman_image_get_data(reference_src);
195*d83cc019SAndroid Build Coastguard Worker
196*d83cc019SAndroid Build Coastguard Worker capture_src = pixman_image_create_bits(
197*d83cc019SAndroid Build Coastguard Worker PIXMAN_x8r8g8b8, w, h,
198*d83cc019SAndroid Build Coastguard Worker (void*)cairo_image_surface_get_data(capture),
199*d83cc019SAndroid Build Coastguard Worker cairo_image_surface_get_stride(capture));
200*d83cc019SAndroid Build Coastguard Worker capture_pixels = (unsigned char *) pixman_image_get_data(capture_src);
201*d83cc019SAndroid Build Coastguard Worker
202*d83cc019SAndroid Build Coastguard Worker /* Collect the absolute error for each color value */
203*d83cc019SAndroid Build Coastguard Worker for (x = 0; x < w; x++) {
204*d83cc019SAndroid Build Coastguard Worker for (y = 0; y < h; y++) {
205*d83cc019SAndroid Build Coastguard Worker p = &capture_pixels[(x + y * w) * 4];
206*d83cc019SAndroid Build Coastguard Worker q = &reference_pixels[(x + y * w) * 4];
207*d83cc019SAndroid Build Coastguard Worker
208*d83cc019SAndroid Build Coastguard Worker for (i = 0; i < 3; i++) {
209*d83cc019SAndroid Build Coastguard Worker diff = (int) p[i] - q[i];
210*d83cc019SAndroid Build Coastguard Worker if (diff < 0)
211*d83cc019SAndroid Build Coastguard Worker diff = -diff;
212*d83cc019SAndroid Build Coastguard Worker
213*d83cc019SAndroid Build Coastguard Worker error_count[i][q[i]][0] += diff;
214*d83cc019SAndroid Build Coastguard Worker error_count[i][q[i]][1]++;
215*d83cc019SAndroid Build Coastguard Worker }
216*d83cc019SAndroid Build Coastguard Worker }
217*d83cc019SAndroid Build Coastguard Worker }
218*d83cc019SAndroid Build Coastguard Worker
219*d83cc019SAndroid Build Coastguard Worker /* Calculate the average absolute error for each color value */
220*d83cc019SAndroid Build Coastguard Worker for (i = 0; i < 250; i++) {
221*d83cc019SAndroid Build Coastguard Worker error_average[0][i] = i;
222*d83cc019SAndroid Build Coastguard Worker
223*d83cc019SAndroid Build Coastguard Worker for (j = 1; j < 4; j++) {
224*d83cc019SAndroid Build Coastguard Worker error_average[j][i] = (double) error_count[j-1][i][0] /
225*d83cc019SAndroid Build Coastguard Worker error_count[j-1][i][1];
226*d83cc019SAndroid Build Coastguard Worker
227*d83cc019SAndroid Build Coastguard Worker if (error_average[j][i] > 60) {
228*d83cc019SAndroid Build Coastguard Worker igt_warn("Error average too high (%f)\n",
229*d83cc019SAndroid Build Coastguard Worker error_average[j][i]);
230*d83cc019SAndroid Build Coastguard Worker
231*d83cc019SAndroid Build Coastguard Worker match = false;
232*d83cc019SAndroid Build Coastguard Worker goto complete;
233*d83cc019SAndroid Build Coastguard Worker }
234*d83cc019SAndroid Build Coastguard Worker }
235*d83cc019SAndroid Build Coastguard Worker }
236*d83cc019SAndroid Build Coastguard Worker
237*d83cc019SAndroid Build Coastguard Worker /*
238*d83cc019SAndroid Build Coastguard Worker * Calculate error trend from linear fit.
239*d83cc019SAndroid Build Coastguard Worker * A DAC-ADC chain is expected to have a linear absolute error on
240*d83cc019SAndroid Build Coastguard Worker * most of its range
241*d83cc019SAndroid Build Coastguard Worker */
242*d83cc019SAndroid Build Coastguard Worker for (i = 1; i < 4; i++) {
243*d83cc019SAndroid Build Coastguard Worker gsl_fit_linear((const double *) &error_average[0], 1,
244*d83cc019SAndroid Build Coastguard Worker (const double *) &error_average[i], 1, 250,
245*d83cc019SAndroid Build Coastguard Worker &c0, &c1, &cov00, &cov01, &cov11, &sumsq);
246*d83cc019SAndroid Build Coastguard Worker
247*d83cc019SAndroid Build Coastguard Worker for (j = 0; j < 250; j++)
248*d83cc019SAndroid Build Coastguard Worker error_trend[j] = c0 + j * c1;
249*d83cc019SAndroid Build Coastguard Worker
250*d83cc019SAndroid Build Coastguard Worker correlation = gsl_stats_correlation((const double *) &error_trend,
251*d83cc019SAndroid Build Coastguard Worker 1,
252*d83cc019SAndroid Build Coastguard Worker (const double *) &error_average[i],
253*d83cc019SAndroid Build Coastguard Worker 1, 250);
254*d83cc019SAndroid Build Coastguard Worker
255*d83cc019SAndroid Build Coastguard Worker if (correlation < 0.985) {
256*d83cc019SAndroid Build Coastguard Worker igt_warn("Error with reference not correlated (%f)\n",
257*d83cc019SAndroid Build Coastguard Worker correlation);
258*d83cc019SAndroid Build Coastguard Worker
259*d83cc019SAndroid Build Coastguard Worker match = false;
260*d83cc019SAndroid Build Coastguard Worker goto complete;
261*d83cc019SAndroid Build Coastguard Worker }
262*d83cc019SAndroid Build Coastguard Worker }
263*d83cc019SAndroid Build Coastguard Worker
264*d83cc019SAndroid Build Coastguard Worker complete:
265*d83cc019SAndroid Build Coastguard Worker pixman_image_unref(reference_src);
266*d83cc019SAndroid Build Coastguard Worker pixman_image_unref(capture_src);
267*d83cc019SAndroid Build Coastguard Worker
268*d83cc019SAndroid Build Coastguard Worker return match;
269*d83cc019SAndroid Build Coastguard Worker }
270*d83cc019SAndroid Build Coastguard Worker
271*d83cc019SAndroid Build Coastguard Worker #define XR24_COLOR_VALUE(data, stride, x, y, c) \
272*d83cc019SAndroid Build Coastguard Worker *((uint8_t *)(data) + (y) * (stride) + 4 * (x) + (c))
273*d83cc019SAndroid Build Coastguard Worker
274*d83cc019SAndroid Build Coastguard Worker /**
275*d83cc019SAndroid Build Coastguard Worker * igt_check_checkerboard_frame_match:
276*d83cc019SAndroid Build Coastguard Worker * @reference: The reference cairo surface
277*d83cc019SAndroid Build Coastguard Worker * @capture: The captured cairo surface
278*d83cc019SAndroid Build Coastguard Worker *
279*d83cc019SAndroid Build Coastguard Worker * Checks that the reference frame matches the captured frame using a
280*d83cc019SAndroid Build Coastguard Worker * method designed for checkerboard patterns. These patterns are made of
281*d83cc019SAndroid Build Coastguard Worker * consecutive rectangular shapes with alternating solid colors.
282*d83cc019SAndroid Build Coastguard Worker *
283*d83cc019SAndroid Build Coastguard Worker * The intent of this method is to cover cases where the captured result is
284*d83cc019SAndroid Build Coastguard Worker * not pixel-perfect due to features such as scaling or YUV conversion and
285*d83cc019SAndroid Build Coastguard Worker * subsampling. Such effects are mostly noticeable on the edges of the
286*d83cc019SAndroid Build Coastguard Worker * patterns, so they are detected and excluded from the comparison.
287*d83cc019SAndroid Build Coastguard Worker *
288*d83cc019SAndroid Build Coastguard Worker * The algorithm works with two major steps. First, the edges of the reference
289*d83cc019SAndroid Build Coastguard Worker * pattern are detected on the x and y axis independently. The detection is done
290*d83cc019SAndroid Build Coastguard Worker * by calculating an absolute difference with a span of a few pixels before and
291*d83cc019SAndroid Build Coastguard Worker * after the current position on the given axis, accumulated for each color
292*d83cc019SAndroid Build Coastguard Worker * component. If the sum is above a given threshold on one of the axis, the
293*d83cc019SAndroid Build Coastguard Worker * position is marked as an edge. In the second step, the pixel values are
294*d83cc019SAndroid Build Coastguard Worker * compared per-component, excluding positions that were marked as edges or
295*d83cc019SAndroid Build Coastguard Worker * that are at a transition between an edge and a non-edge. An error threshold
296*d83cc019SAndroid Build Coastguard Worker * (for each individual color component) is used to mark the position as
297*d83cc019SAndroid Build Coastguard Worker * erroneous or not. The ratio of erroneous pixels over compared pixels (that
298*d83cc019SAndroid Build Coastguard Worker * does not count excluded pixels) is then calculated and compared to the error
299*d83cc019SAndroid Build Coastguard Worker * rate threshold to determine whether the frames match or not.
300*d83cc019SAndroid Build Coastguard Worker *
301*d83cc019SAndroid Build Coastguard Worker * Returns: a boolean indicating whether the frames match
302*d83cc019SAndroid Build Coastguard Worker */
igt_check_checkerboard_frame_match(cairo_surface_t * reference,cairo_surface_t * capture)303*d83cc019SAndroid Build Coastguard Worker bool igt_check_checkerboard_frame_match(cairo_surface_t *reference,
304*d83cc019SAndroid Build Coastguard Worker cairo_surface_t *capture)
305*d83cc019SAndroid Build Coastguard Worker {
306*d83cc019SAndroid Build Coastguard Worker unsigned int width, height, ref_stride, cap_stride;
307*d83cc019SAndroid Build Coastguard Worker void *ref_data, *cap_data;
308*d83cc019SAndroid Build Coastguard Worker unsigned char *edges_map;
309*d83cc019SAndroid Build Coastguard Worker unsigned int x, y, c;
310*d83cc019SAndroid Build Coastguard Worker unsigned int errors = 0, pixels = 0;
311*d83cc019SAndroid Build Coastguard Worker unsigned int edge_threshold = 100;
312*d83cc019SAndroid Build Coastguard Worker unsigned int color_error_threshold = 24;
313*d83cc019SAndroid Build Coastguard Worker double error_rate_threshold = 0.01;
314*d83cc019SAndroid Build Coastguard Worker double error_rate;
315*d83cc019SAndroid Build Coastguard Worker unsigned int span = 2;
316*d83cc019SAndroid Build Coastguard Worker bool match = false;
317*d83cc019SAndroid Build Coastguard Worker
318*d83cc019SAndroid Build Coastguard Worker width = cairo_image_surface_get_width(reference);
319*d83cc019SAndroid Build Coastguard Worker height = cairo_image_surface_get_height(reference);
320*d83cc019SAndroid Build Coastguard Worker
321*d83cc019SAndroid Build Coastguard Worker ref_stride = cairo_image_surface_get_stride(reference);
322*d83cc019SAndroid Build Coastguard Worker ref_data = cairo_image_surface_get_data(reference);
323*d83cc019SAndroid Build Coastguard Worker igt_assert(ref_data);
324*d83cc019SAndroid Build Coastguard Worker
325*d83cc019SAndroid Build Coastguard Worker cap_stride = cairo_image_surface_get_stride(capture);
326*d83cc019SAndroid Build Coastguard Worker cap_data = cairo_image_surface_get_data(capture);
327*d83cc019SAndroid Build Coastguard Worker igt_assert(cap_data);
328*d83cc019SAndroid Build Coastguard Worker
329*d83cc019SAndroid Build Coastguard Worker edges_map = calloc(1, width * height);
330*d83cc019SAndroid Build Coastguard Worker igt_assert(edges_map);
331*d83cc019SAndroid Build Coastguard Worker
332*d83cc019SAndroid Build Coastguard Worker /* First pass to detect the pattern edges. */
333*d83cc019SAndroid Build Coastguard Worker for (y = 0; y < height; y++) {
334*d83cc019SAndroid Build Coastguard Worker if (y < span || y > (height - span - 1))
335*d83cc019SAndroid Build Coastguard Worker continue;
336*d83cc019SAndroid Build Coastguard Worker
337*d83cc019SAndroid Build Coastguard Worker for (x = 0; x < width; x++) {
338*d83cc019SAndroid Build Coastguard Worker unsigned int xdiff = 0, ydiff = 0;
339*d83cc019SAndroid Build Coastguard Worker
340*d83cc019SAndroid Build Coastguard Worker if (x < span || x > (width - span - 1))
341*d83cc019SAndroid Build Coastguard Worker continue;
342*d83cc019SAndroid Build Coastguard Worker
343*d83cc019SAndroid Build Coastguard Worker for (c = 0; c < 3; c++) {
344*d83cc019SAndroid Build Coastguard Worker xdiff += abs(XR24_COLOR_VALUE(ref_data, ref_stride, x + span, y, c) -
345*d83cc019SAndroid Build Coastguard Worker XR24_COLOR_VALUE(ref_data, ref_stride, x - span, y, c));
346*d83cc019SAndroid Build Coastguard Worker ydiff += abs(XR24_COLOR_VALUE(ref_data, ref_stride, x, y + span, c) -
347*d83cc019SAndroid Build Coastguard Worker XR24_COLOR_VALUE(ref_data, ref_stride, x, y - span, c));
348*d83cc019SAndroid Build Coastguard Worker }
349*d83cc019SAndroid Build Coastguard Worker
350*d83cc019SAndroid Build Coastguard Worker edges_map[y * width + x] = (xdiff > edge_threshold ||
351*d83cc019SAndroid Build Coastguard Worker ydiff > edge_threshold);
352*d83cc019SAndroid Build Coastguard Worker }
353*d83cc019SAndroid Build Coastguard Worker }
354*d83cc019SAndroid Build Coastguard Worker
355*d83cc019SAndroid Build Coastguard Worker /* Second pass to detect errors. */
356*d83cc019SAndroid Build Coastguard Worker for (y = 0; y < height; y++) {
357*d83cc019SAndroid Build Coastguard Worker for (x = 0; x < width; x++) {
358*d83cc019SAndroid Build Coastguard Worker bool error = false;
359*d83cc019SAndroid Build Coastguard Worker
360*d83cc019SAndroid Build Coastguard Worker if (edges_map[y * width + x])
361*d83cc019SAndroid Build Coastguard Worker continue;
362*d83cc019SAndroid Build Coastguard Worker
363*d83cc019SAndroid Build Coastguard Worker for (c = 0; c < 3; c++) {
364*d83cc019SAndroid Build Coastguard Worker unsigned int diff;
365*d83cc019SAndroid Build Coastguard Worker
366*d83cc019SAndroid Build Coastguard Worker /* Compare the reference and capture values. */
367*d83cc019SAndroid Build Coastguard Worker diff = abs(XR24_COLOR_VALUE(ref_data, ref_stride, x, y, c) -
368*d83cc019SAndroid Build Coastguard Worker XR24_COLOR_VALUE(cap_data, cap_stride, x, y, c));
369*d83cc019SAndroid Build Coastguard Worker
370*d83cc019SAndroid Build Coastguard Worker if (diff > color_error_threshold)
371*d83cc019SAndroid Build Coastguard Worker error = true;
372*d83cc019SAndroid Build Coastguard Worker }
373*d83cc019SAndroid Build Coastguard Worker
374*d83cc019SAndroid Build Coastguard Worker /* Allow error if coming on or off an edge (on x). */
375*d83cc019SAndroid Build Coastguard Worker if (error && x >= span && x <= (width - span - 1) &&
376*d83cc019SAndroid Build Coastguard Worker edges_map[y * width + (x - span)] !=
377*d83cc019SAndroid Build Coastguard Worker edges_map[y * width + (x + span)])
378*d83cc019SAndroid Build Coastguard Worker continue;
379*d83cc019SAndroid Build Coastguard Worker
380*d83cc019SAndroid Build Coastguard Worker /* Allow error if coming on or off an edge (on y). */
381*d83cc019SAndroid Build Coastguard Worker if (error && y >= span && y <= (height - span - 1) &&
382*d83cc019SAndroid Build Coastguard Worker edges_map[(y - span) * width + x] !=
383*d83cc019SAndroid Build Coastguard Worker edges_map[(y + span) * width + x] && error)
384*d83cc019SAndroid Build Coastguard Worker continue;
385*d83cc019SAndroid Build Coastguard Worker
386*d83cc019SAndroid Build Coastguard Worker if (error)
387*d83cc019SAndroid Build Coastguard Worker errors++;
388*d83cc019SAndroid Build Coastguard Worker
389*d83cc019SAndroid Build Coastguard Worker pixels++;
390*d83cc019SAndroid Build Coastguard Worker }
391*d83cc019SAndroid Build Coastguard Worker }
392*d83cc019SAndroid Build Coastguard Worker
393*d83cc019SAndroid Build Coastguard Worker free(edges_map);
394*d83cc019SAndroid Build Coastguard Worker
395*d83cc019SAndroid Build Coastguard Worker error_rate = (double) errors / pixels;
396*d83cc019SAndroid Build Coastguard Worker
397*d83cc019SAndroid Build Coastguard Worker if (error_rate < error_rate_threshold)
398*d83cc019SAndroid Build Coastguard Worker match = true;
399*d83cc019SAndroid Build Coastguard Worker
400*d83cc019SAndroid Build Coastguard Worker igt_debug("Checkerboard pattern %s with error rate %f %%\n",
401*d83cc019SAndroid Build Coastguard Worker match ? "matched" : "not matched", error_rate * 100);
402*d83cc019SAndroid Build Coastguard Worker
403*d83cc019SAndroid Build Coastguard Worker return match;
404*d83cc019SAndroid Build Coastguard Worker }
405