1#include <stdint.h>
2#include <stdio.h>
3#include <string.h>
4#include <png.h>
5
6#include "test.h"
7
8#define MAX_DELTA 3
9
10int pixel_difference(uint32_t a, uint32_t b)
11{
12	int max = 0;
13	int i;
14
15	for (i = 0; i < 32; i += 8) {
16		uint8_t ac = (a >> i) & 0xff;
17		uint8_t bc = (b >> i) & 0xff;
18		int d;
19
20		if (ac > bc)
21			d = ac - bc;
22		else
23			d = bc - ac;
24		if (d > max)
25			max = d;
26	}
27
28	return max;
29}
30
31static void
32show_pixels(char *buf,
33	    const XImage *out, const XImage *ref,
34	    int x, int y, int w, int h)
35{
36	int i, j, len = 0;
37
38	for (j = y - 2; j <= y + 2; j++) {
39		if (j < 0 || j >= h)
40			continue;
41
42		for (i = x - 2; i <= x + 2; i++) {
43			if (i < 0 || i >= w)
44				continue;
45
46			len += sprintf(buf+len,
47				       "%08x ",
48				       *(uint32_t*)(out->data +
49						    j*out->bytes_per_line +
50						    i*out->bits_per_pixel/8));
51		}
52
53		len += sprintf(buf+len, "\t");
54
55		for (i = x - 2; i <= x + 2; i++) {
56			if (i < 0 || i >= w)
57				continue;
58
59			len += sprintf(buf+len,
60				       "%08x ",
61				       *(uint32_t*)(ref->data +
62						    j*out->bytes_per_line +
63						    i*out->bits_per_pixel/8));
64		}
65
66		len += sprintf(buf+len, "\n");
67	}
68}
69
70static void test_compare_fallback(struct test *t,
71				  Drawable out_draw, XRenderPictFormat *out_format,
72				  Drawable ref_draw, XRenderPictFormat *ref_format,
73				  int x, int y, int w, int h)
74{
75	XImage *out_image, *ref_image;
76	char *out, *ref;
77	char buf[600];
78	uint32_t mask;
79	int i, j;
80
81	die_unless(out_format->depth == ref_format->depth);
82
83	out_image = XGetImage(t->out.dpy, out_draw,
84			       x, y, w, h,
85			       AllPlanes, ZPixmap);
86	out = out_image->data;
87
88	ref_image = XGetImage(t->ref.dpy, ref_draw,
89			      x, y, w, h,
90			      AllPlanes, ZPixmap);
91	ref = ref_image->data;
92
93	mask = depth_mask(out_image->depth);
94
95	/* Start with an exact comparison. However, one quicky desires
96	 * a fuzzy comparator to hide hardware inaccuracies...
97	 */
98	for (j = 0; j < h; j++) {
99		for (i = 0; i < w; i++) {
100			uint32_t a = ((uint32_t *)out)[i] & mask;
101			uint32_t b = ((uint32_t *)ref)[i] & mask;
102			if (a != b && pixel_difference(a, b) > MAX_DELTA) {
103				show_pixels(buf,
104					    out_image, ref_image,
105					    i, j, w, h);
106				die("discrepancy found at (%d+%d, %d+%d): found %08x, expected %08x (delta: %d)\n%s",
107				    x,i, y,j, a, b, pixel_difference(a, b), buf);
108			}
109		}
110		out += out_image->bytes_per_line;
111		ref += ref_image->bytes_per_line;
112	}
113
114	XDestroyImage(out_image);
115	XDestroyImage(ref_image);
116}
117
118static void
119unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
120{
121	unsigned int i;
122
123	for (i = 0; i < row_info->rowbytes; i += 4) {
124		uint8_t *b = &data[i];
125		uint32_t pixel;
126		uint8_t  alpha;
127
128		memcpy (&pixel, b, sizeof (uint32_t));
129		alpha = (pixel & 0xff000000) >> 24;
130		if (alpha == 0) {
131			b[0] = (pixel & 0xff0000) >> 16;
132			b[1] = (pixel & 0x00ff00) >>  8;
133			b[2] = (pixel & 0x0000ff) >>  0;
134			b[3] = 0xff;
135		} else {
136			b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
137			b[1] = (((pixel & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
138			b[2] = (((pixel & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
139			b[3] = alpha;
140		}
141	}
142}
143
144static void save_image(XImage *image, const char *filename)
145{
146	FILE *file;
147	png_struct *png = NULL;
148	png_info *info = NULL;
149	png_byte **rows = NULL;
150	int i;
151
152	file = fopen(filename, "w");
153	if (file == NULL)
154		return;
155
156	png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
157	if (png == NULL)
158		goto out;
159
160	info = png_create_info_struct(png);
161	if (info == NULL)
162		goto out;
163
164	rows = png_malloc(png, sizeof(png_byte *) * image->height);
165	if (rows == NULL)
166		goto out;
167	for (i = 0; i < image->height; i++)
168		rows[i] = (png_byte *)(image->data + image->bytes_per_line * i);
169
170	if (setjmp(png_jmpbuf(png)))
171		goto out;
172
173	png_set_IHDR(png, info,
174		     image->width, image->height, 8,
175		     PNG_COLOR_TYPE_RGB_ALPHA,
176		     PNG_INTERLACE_NONE,
177		     PNG_COMPRESSION_TYPE_DEFAULT,
178		     PNG_FILTER_TYPE_DEFAULT);
179
180	png_init_io(png, file);
181	png_write_info(png, info);
182	png_set_write_user_transform_fn(png, unpremultiply_data);
183	png_write_image(png, rows);
184	png_write_end(png, info);
185
186out:
187	if (rows)
188		png_free(png, rows);
189	png_destroy_write_struct(&png, &info);
190	fclose(file);
191}
192
193void test_compare(struct test *t,
194		  Drawable out_draw, XRenderPictFormat *out_format,
195		  Drawable ref_draw, XRenderPictFormat *ref_format,
196		  int x, int y, int w, int h,
197		  const char *info)
198{
199	XImage out_image, ref_image;
200	Pixmap tmp;
201	char *out, *ref;
202	char buf[600];
203	uint32_t mask;
204	int i, j;
205	XGCValues gcv;
206	GC gc;
207
208	if (w * h * 4 > t->out.max_shm_size)
209		return test_compare_fallback(t,
210					     out_draw, out_format,
211					     ref_draw, ref_format,
212					     x, y, w, h);
213
214	test_init_image(&out_image, &t->out.shm, out_format, w, h);
215	test_init_image(&ref_image, &t->ref.shm, ref_format, w, h);
216
217	gcv.graphics_exposures = 0;
218
219	die_unless(out_image.depth == ref_image.depth);
220	die_unless(out_image.bits_per_pixel == ref_image.bits_per_pixel);
221	die_unless(out_image.bits_per_pixel == 32);
222
223	mask = depth_mask(out_image.depth);
224
225	tmp = XCreatePixmap(t->out.dpy, out_draw, w, h, out_image.depth);
226	gc = XCreateGC(t->out.dpy, tmp, GCGraphicsExposures, &gcv);
227	XCopyArea(t->out.dpy, out_draw, tmp, gc, x, y, w, h, 0, 0);
228	XShmGetImage(t->out.dpy, tmp, &out_image, 0, 0, AllPlanes);
229	XFreeGC(t->out.dpy, gc);
230	XFreePixmap(t->out.dpy, tmp);
231	out = out_image.data;
232
233	tmp = XCreatePixmap(t->ref.dpy, ref_draw, w, h, ref_image.depth);
234	gc = XCreateGC(t->ref.dpy, tmp, GCGraphicsExposures, &gcv);
235	XCopyArea(t->ref.dpy, ref_draw, tmp, gc, x, y, w, h, 0, 0);
236	XShmGetImage(t->ref.dpy, tmp, &ref_image, 0, 0, AllPlanes);
237	XFreeGC(t->ref.dpy, gc);
238	XFreePixmap(t->ref.dpy, tmp);
239	ref = ref_image.data;
240
241	/* Start with an exact comparison. However, one quicky desires
242	 * a fuzzy comparator to hide hardware inaccuracies...
243	 */
244	for (j = 0; j < h; j++) {
245		for (i = 0; i < w; i++) {
246			uint32_t a = ((uint32_t *)out)[i] & mask;
247			uint32_t b = ((uint32_t *)ref)[i] & mask;
248			if (a != b && pixel_difference(a, b) > MAX_DELTA) {
249				show_pixels(buf,
250					    &out_image, &ref_image,
251					    i, j, w, h);
252				save_image(&out_image, "out.png");
253				save_image(&ref_image,  "ref.png");
254				die("discrepancy found at (%d+%d, %d+%d): found %08x, expected %08x (delta: %d)\n%s%s\n",
255				    x,i, y,j, a, b, pixel_difference(a, b), buf, info);
256			}
257		}
258		out += out_image.bytes_per_line;
259		ref += ref_image.bytes_per_line;
260	}
261}
262
263static int
264_native_byte_order_lsb(void)
265{
266	int x = 1;
267	return *((char *) &x) == 1;
268}
269
270void
271test_init_image(XImage *ximage,
272		XShmSegmentInfo *shm,
273		XRenderPictFormat *format,
274		int width, int height)
275{
276	int native_byte_order = _native_byte_order_lsb() ? LSBFirst : MSBFirst;
277
278	ximage->width = width;
279	ximage->height = height;
280	ximage->format = ZPixmap;
281	ximage->data = shm->shmaddr;
282	ximage->obdata = (void *)shm;
283	ximage->byte_order = native_byte_order;
284	ximage->bitmap_unit = 32;
285	ximage->bitmap_bit_order = native_byte_order;
286	ximage->bitmap_pad = 32;
287	ximage->depth = format->depth;
288	ximage->bytes_per_line = 4*width;
289	ximage->bits_per_pixel = 32;
290	ximage->red_mask = 0xff << 16;
291	ximage->green_mask = 0xff << 8;
292	ximage->blue_mask = 0xff << 0;
293	ximage->xoffset = 0;
294
295	XInitImage(ximage);
296}
297