1/*
2 * Copyright (C) 2011,2021 Red Hat Inc.
3 *
4 * Author:
5 * Benjamin Otte <otte@gnome.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "config.h"
22
23#include "reftest-compare.h"
24
25/* Compares two GDK_MEMORY_DEFAULT buffers, returning NULL if the
26 * buffers are equal or a surface containing a diff between the two
27 * surfaces.
28 *
29 * This function is originally from cairo:test/buffer-diff.c.
30 * Copyright © 2004 Richard D. Worth
31 */
32static GdkTexture *
33buffer_diff_core (const guchar *buf_a,
34 int stride_a,
35 const guchar *buf_b,
36 int stride_b,
37 int width,
38 int height)
39{
40 int x, y;
41 guchar *buf_diff = NULL;
42 int stride_diff = 0;
43 GdkTexture *diff = NULL;
44
45 for (y = 0; y < height; y++)
46 {
47 const guint32 *row_a = (const guint32 *) (buf_a + y * stride_a);
48 const guint32 *row_b = (const guint32 *) (buf_b + y * stride_b);
49 guint32 *row = (guint32 *) (buf_diff + y * stride_diff);
50
51 for (x = 0; x < width; x++)
52 {
53 int channel;
54 guint32 diff_pixel = 0;
55
56 /* check if the pixels are the same */
57 if (row_a[x] == row_b[x])
58 continue;
59
60 /* even if they're not literally the same, fully-transparent
61 * pixels are effectively the same regardless of colour */
62 if ((row_a[x] & 0xff000000) == 0 && (row_b[x] & 0xff000000) == 0)
63 continue;
64
65 if (diff == NULL)
66 {
67 GBytes *bytes;
68
69 stride_diff = 4 * width;
70 buf_diff = g_malloc0_n (n_blocks: stride_diff, n_block_bytes: height);
71 bytes = g_bytes_new_take (data: buf_diff, size: stride_diff * height);
72 diff = gdk_memory_texture_new (width, height,
73 GDK_MEMORY_DEFAULT,
74 bytes,
75 stride: stride_diff);
76 row = (guint32 *) (buf_diff + y * stride_diff);
77 }
78
79 /* calculate a difference value for all 4 channels */
80 for (channel = 0; channel < 4; channel++)
81 {
82 int value_a = (row_a[x] >> (channel*8)) & 0xff;
83 int value_b = (row_b[x] >> (channel*8)) & 0xff;
84 guint channel_diff;
85
86 channel_diff = ABS (value_a - value_b);
87 channel_diff *= 4; /* emphasize */
88 if (channel_diff)
89 channel_diff += 128; /* make sure it's visible */
90 if (channel_diff > 255)
91 channel_diff = 255;
92 diff_pixel |= channel_diff << (channel * 8);
93 }
94
95 if ((diff_pixel & 0x00ffffff) == 0)
96 {
97 /* alpha only difference, convert to luminance */
98 guint8 alpha = diff_pixel >> 24;
99 diff_pixel = alpha * 0x010101;
100 }
101 /* make the pixel fully opaque */
102 diff_pixel |= 0xff000000;
103
104 row[x] = diff_pixel;
105 }
106 }
107
108 return diff;
109}
110
111GdkTexture *
112reftest_compare_textures (GdkTexture *texture1,
113 GdkTexture *texture2)
114{
115 int w, h;
116 guchar *data1, *data2;
117 GdkTexture *diff;
118
119 w = MAX (gdk_texture_get_width (texture1), gdk_texture_get_width (texture2));
120 h = MAX (gdk_texture_get_height (texture1), gdk_texture_get_height (texture2));
121
122 data1 = g_malloc_n (n_blocks: w * 4, n_block_bytes: h);
123 gdk_texture_download (texture: texture1, data: data1, stride: w * 4);
124 data2 = g_malloc_n (n_blocks: w * 4, n_block_bytes: h);
125 gdk_texture_download (texture: texture2, data: data2, stride: w * 4);
126
127 diff = buffer_diff_core (buf_a: data1, stride_a: w * 4,
128 buf_b: data2, stride_b: w * 4,
129 width: w, height: h);
130
131 g_free (mem: data1);
132 g_free (mem: data2);
133
134 return diff;
135}
136

source code of gtk/testsuite/reftests/reftest-compare.c