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 | */ |
32 | static GdkTexture * |
33 | buffer_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 | |
111 | GdkTexture * |
112 | reftest_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 | |