1 | /* -*- Mode: C; c-basic-offset: 2; -*- */ |
2 | /* GdkPixbuf library - test loaders |
3 | * |
4 | * Copyright (C) 2014 Red Hat, Inc. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. |
19 | * |
20 | * Author: Matthias Clasen |
21 | */ |
22 | |
23 | #include "config.h" |
24 | #include "test-common.h" |
25 | #include "gdk-pixbuf/gdk-pixbuf.h" |
26 | |
27 | #include <string.h> |
28 | |
29 | /* Checkerboard of black and white pxels (actually (1,1,1) and (255,255,255) |
30 | * so they average to (128,128,128)) */ |
31 | GdkPixbuf * |
32 | make_checkerboard (int width, int height) |
33 | { |
34 | GdkPixbuf *checkerboard; |
35 | guint x, y; |
36 | guchar *row; /* Pointer to start of row of pixels within the image */ |
37 | guchar *pixel; /* Pointer to current pixel data in row */ |
38 | |
39 | checkerboard = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, has_alpha: 0, bits_per_sample: 8, width, height); |
40 | g_assert_nonnull (checkerboard); |
41 | |
42 | for (y = 0, row = gdk_pixbuf_get_pixels (pixbuf: checkerboard); |
43 | y < height; |
44 | y++, row += gdk_pixbuf_get_rowstride (pixbuf: checkerboard)) |
45 | { |
46 | for (x = 0, pixel = row; |
47 | x < width; |
48 | x++, pixel += gdk_pixbuf_get_n_channels (pixbuf: checkerboard)) |
49 | { |
50 | pixel[0] = pixel[1] = pixel[2] = (x ^ y) & 1 ? 1 : 255; |
51 | } |
52 | } |
53 | |
54 | return checkerboard; |
55 | } |
56 | |
57 | /* Image where all the pixels have different colours */ |
58 | GdkPixbuf * |
59 | make_rg (int width, int height) |
60 | { |
61 | GdkPixbuf *pixbuf; |
62 | guint x, y; |
63 | guchar *row; /* Pointer to start of row of pixels within the image */ |
64 | guchar *pixel; /* Pointer to current pixel data in row */ |
65 | |
66 | /* Make a source image whose pixels are all of different colors */ |
67 | pixbuf = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, has_alpha: 0, bits_per_sample: 8, width, height); |
68 | g_assert_nonnull (pixbuf); |
69 | |
70 | for (y = 0, row = gdk_pixbuf_get_pixels (pixbuf); |
71 | y < height; |
72 | y++, row += gdk_pixbuf_get_rowstride (pixbuf)) |
73 | { |
74 | for (x = 0, pixel = row; |
75 | x < width; |
76 | x++, pixel += gdk_pixbuf_get_n_channels (pixbuf)) |
77 | { |
78 | pixel[0] = x & 255; pixel[1] = y & 255; |
79 | /* If image > 256 pixels wide/high put the extra bits in the last pixel */ |
80 | pixel[2] = ((x >> 8) & 15) | ((( y >> 8) & 15) << 4); |
81 | } |
82 | } |
83 | |
84 | return pixbuf; |
85 | } |
86 | |
87 | gboolean |
88 | find_format (const gchar *filename, gchar **found_format) |
89 | { |
90 | GSList *formats, *l; |
91 | gboolean retval; |
92 | |
93 | retval = FALSE; |
94 | formats = gdk_pixbuf_get_formats (); |
95 | for (l = formats; l; l = l->next) |
96 | { |
97 | GdkPixbufFormat *format = l->data; |
98 | char **extensions = gdk_pixbuf_format_get_extensions (format); |
99 | gint i; |
100 | |
101 | for (i = 0; extensions[i]; i++) |
102 | { |
103 | if (g_str_has_suffix (str: filename, suffix: extensions[i])) |
104 | { |
105 | if (found_format != NULL) |
106 | *found_format = gdk_pixbuf_format_get_name (format); |
107 | retval = TRUE; |
108 | break; |
109 | } |
110 | } |
111 | |
112 | g_strfreev (str_array: extensions); |
113 | if (retval) |
114 | break; |
115 | } |
116 | g_slist_free (list: formats); |
117 | |
118 | return retval; |
119 | } |
120 | |
121 | gboolean |
122 | format_supported (const gchar *filename) |
123 | { |
124 | return find_format(filename, NULL); |
125 | } |
126 | |
127 | gboolean |
128 | file_supported (GFile *file) |
129 | { |
130 | char *uri; |
131 | gboolean result; |
132 | |
133 | uri = g_file_get_uri (file); |
134 | |
135 | result = format_supported (filename: uri); |
136 | |
137 | g_free (mem: uri); |
138 | |
139 | return result; |
140 | } |
141 | |
142 | gboolean |
143 | skip_if_insufficient_memory (GError **err) |
144 | { |
145 | if (*err && g_error_matches (error: *err, GDK_PIXBUF_ERROR, code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY)) |
146 | { |
147 | g_test_skip (msg: (*err)->message); |
148 | g_error_free (error: *err); |
149 | *err = NULL; |
150 | return TRUE; |
151 | } |
152 | |
153 | return FALSE; |
154 | } |
155 | |
156 | gboolean |
157 | pixdata_equal (GdkPixbuf *test, |
158 | GdkPixbuf *ref, |
159 | GError **error) |
160 | { |
161 | if (gdk_pixbuf_get_colorspace (pixbuf: test) != gdk_pixbuf_get_colorspace (pixbuf: ref)) |
162 | { |
163 | g_set_error (err: error, GDK_PIXBUF_ERROR, code: 0, format: "Image colorspace is %d but should be %d" , |
164 | gdk_pixbuf_get_colorspace (pixbuf: test), gdk_pixbuf_get_colorspace (pixbuf: ref)); |
165 | return FALSE; |
166 | } |
167 | |
168 | if (gdk_pixbuf_get_n_channels (pixbuf: test) != gdk_pixbuf_get_n_channels (pixbuf: ref)) |
169 | { |
170 | g_set_error (err: error, GDK_PIXBUF_ERROR, code: 0, |
171 | format: "has %u channels but should have %u" , |
172 | gdk_pixbuf_get_n_channels (pixbuf: test), gdk_pixbuf_get_n_channels (pixbuf: ref)); |
173 | return FALSE; |
174 | } |
175 | |
176 | if (gdk_pixbuf_get_bits_per_sample (pixbuf: test) != gdk_pixbuf_get_bits_per_sample (pixbuf: ref)) |
177 | { |
178 | g_set_error (err: error, GDK_PIXBUF_ERROR, code: 0, |
179 | format: "Image is %u bits per sample but should be %u bits per sample" , |
180 | gdk_pixbuf_get_bits_per_sample (pixbuf: test), gdk_pixbuf_get_bits_per_sample (pixbuf: ref)); |
181 | return FALSE; |
182 | } |
183 | |
184 | if (gdk_pixbuf_get_width (pixbuf: test) != gdk_pixbuf_get_width (pixbuf: ref) || |
185 | gdk_pixbuf_get_height (pixbuf: test) != gdk_pixbuf_get_height (pixbuf: ref)) |
186 | { |
187 | g_set_error (err: error, GDK_PIXBUF_ERROR, code: 0, |
188 | format: "Image size is %dx%d but should be %dx%d" , |
189 | gdk_pixbuf_get_width (pixbuf: test), gdk_pixbuf_get_height (pixbuf: test), |
190 | gdk_pixbuf_get_width (pixbuf: ref), gdk_pixbuf_get_height (pixbuf: ref)); |
191 | return FALSE; |
192 | } |
193 | |
194 | if (gdk_pixbuf_get_rowstride (pixbuf: test) != gdk_pixbuf_get_rowstride (pixbuf: ref)) |
195 | { |
196 | g_set_error (err: error, GDK_PIXBUF_ERROR, code: 0, |
197 | format: "Image rowstrides is %u bytes but should be %u bytes" , |
198 | gdk_pixbuf_get_rowstride (pixbuf: test), gdk_pixbuf_get_rowstride (pixbuf: ref)); |
199 | return FALSE; |
200 | } |
201 | |
202 | if (memcmp (s1: gdk_pixbuf_get_pixels (pixbuf: test), s2: gdk_pixbuf_get_pixels (pixbuf: ref), |
203 | n: gdk_pixbuf_get_byte_length (pixbuf: test)) != 0) |
204 | { |
205 | gint x, y, width, height, n_channels, rowstride; |
206 | const guchar *test_pixels, *ref_pixels; |
207 | |
208 | rowstride = gdk_pixbuf_get_rowstride (pixbuf: test); |
209 | n_channels = gdk_pixbuf_get_n_channels (pixbuf: test); |
210 | width = gdk_pixbuf_get_width (pixbuf: test); |
211 | height = gdk_pixbuf_get_height (pixbuf: test); |
212 | test_pixels = gdk_pixbuf_get_pixels (pixbuf: test); |
213 | ref_pixels = gdk_pixbuf_get_pixels (pixbuf: ref); |
214 | |
215 | g_assert_cmpint (width, >=, 0); |
216 | g_assert_cmpint (height, >=, 0); |
217 | g_assert_cmpint (n_channels, >=, 0); |
218 | |
219 | for (y = 0; y < height; y++) |
220 | { |
221 | for (x = 0; x < width; x++) |
222 | { |
223 | if (memcmp (s1: &test_pixels[x * n_channels], s2: &ref_pixels[x * n_channels], n: n_channels) != 0) |
224 | { |
225 | if (n_channels == 4) |
226 | { |
227 | g_set_error (err: error, GDK_PIXBUF_ERROR, code: 0, format: "Image data at %ux%u is #%02X%02X%02X%02X, but should be #%02X%02X%02X%02X" , |
228 | x, y, |
229 | test_pixels[x * n_channels + 0], test_pixels[x * n_channels + 1], test_pixels[x * n_channels + 2], test_pixels[x * n_channels + 3], |
230 | ref_pixels[x * n_channels + 0], ref_pixels[x * n_channels + 1], ref_pixels[x * n_channels + 2], ref_pixels[x * n_channels + 3]); |
231 | } |
232 | else if (n_channels == 3) |
233 | { |
234 | g_set_error (err: error, GDK_PIXBUF_ERROR, code: 0, format: "Image data at %ux%u is #%02X%02X%02X, but should be #%02X%02X%02X" , |
235 | x, y, |
236 | test_pixels[x * n_channels + 0], test_pixels[x * n_channels + 1], test_pixels[x * n_channels + 2], |
237 | ref_pixels[x * n_channels + 0], ref_pixels[x * n_channels + 1], ref_pixels[x * n_channels + 2]); |
238 | } |
239 | else |
240 | { |
241 | g_set_error (err: error, GDK_PIXBUF_ERROR, code: 0, format: "Image data differ at %ux%u" , x, y); |
242 | } |
243 | return FALSE; |
244 | } |
245 | } |
246 | test_pixels += rowstride; |
247 | ref_pixels += rowstride; |
248 | } |
249 | } |
250 | |
251 | return TRUE; |
252 | } |
253 | |
254 | static int |
255 | compare_files (gconstpointer a, gconstpointer b) |
256 | { |
257 | GFile *file1 = G_FILE (a); |
258 | GFile *file2 = G_FILE (b); |
259 | char *uri1, *uri2; |
260 | int result; |
261 | |
262 | uri1 = g_file_get_uri (file: file1); |
263 | uri2 = g_file_get_uri (file: file2); |
264 | |
265 | result = strcmp (s1: uri1, s2: uri2); |
266 | |
267 | g_free (mem: uri1); |
268 | g_free (mem: uri2); |
269 | |
270 | return result; |
271 | } |
272 | |
273 | void |
274 | add_test_for_all_images (const gchar *prefix, |
275 | GFile *base, |
276 | GFile *file, |
277 | GTestDataFunc test_func, |
278 | AddTestFunc add_test_func) |
279 | { |
280 | GFileEnumerator *enumerator; |
281 | GFileInfo *info; |
282 | GList *l, *files; |
283 | GError *error = NULL; |
284 | |
285 | |
286 | if (g_file_query_file_type (file, flags: 0, NULL) != G_FILE_TYPE_DIRECTORY) |
287 | { |
288 | gchar *test_path; |
289 | gchar *relative_path; |
290 | |
291 | if (base) |
292 | relative_path = g_file_get_relative_path (parent: base, descendant: file); |
293 | else |
294 | relative_path = g_file_get_path (file); |
295 | |
296 | test_path = g_strconcat (string1: prefix, "/" , relative_path, NULL); |
297 | |
298 | g_test_add_data_func_full (testpath: test_path, g_object_ref (file), test_func, data_free_func: g_object_unref); |
299 | g_free (mem: relative_path); |
300 | g_free (mem: test_path); |
301 | return; |
302 | } |
303 | |
304 | |
305 | enumerator = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, flags: 0, NULL, error: &error); |
306 | g_assert_no_error (error); |
307 | files = NULL; |
308 | |
309 | while ((info = g_file_enumerator_next_file (enumerator, NULL, error: &error))) |
310 | { |
311 | GFile *next_file = g_file_get_child (file, name: g_file_info_get_name (info)); |
312 | |
313 | if (add_test_func == NULL || add_test_func (next_file)) |
314 | { |
315 | files = g_list_prepend (list: files, g_object_ref (next_file)); |
316 | } |
317 | |
318 | g_object_unref (object: next_file); |
319 | g_object_unref (object: info); |
320 | } |
321 | |
322 | g_assert_no_error (error); |
323 | g_object_unref (object: enumerator); |
324 | |
325 | files = g_list_sort (list: files, compare_func: compare_files); |
326 | |
327 | for (l = files; l; l = l->next) |
328 | { |
329 | add_test_for_all_images (prefix, base, file: l->data, test_func, add_test_func); |
330 | } |
331 | |
332 | g_list_free_full (list: files, free_func: g_object_unref); |
333 | } |
334 | |