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 
pixel_difference(uint32_t a,uint32_t b)10 int 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 
31 static void
show_pixels(char * buf,const XImage * out,const XImage * ref,int x,int y,int w,int h)32 show_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 
test_compare_fallback(struct test * t,Drawable out_draw,XRenderPictFormat * out_format,Drawable ref_draw,XRenderPictFormat * ref_format,int x,int y,int w,int h)70 static 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 
118 static void
unpremultiply_data(png_structp png,png_row_infop row_info,png_bytep data)119 unpremultiply_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 
save_image(XImage * image,const char * filename)144 static 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 
186 out:
187 	if (rows)
188 		png_free(png, rows);
189 	png_destroy_write_struct(&png, &info);
190 	fclose(file);
191 }
192 
test_compare(struct test * t,Drawable out_draw,XRenderPictFormat * out_format,Drawable ref_draw,XRenderPictFormat * ref_format,int x,int y,int w,int h,const char * info)193 void 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 	uint32_t *out, *ref;
201 	char buf[600];
202 	uint32_t mask;
203 	int i, j;
204 
205 	if (w * h * 4 > t->out.max_shm_size)
206 		return test_compare_fallback(t,
207 					     out_draw, out_format,
208 					     ref_draw, ref_format,
209 					     x, y, w, h);
210 
211 	test_init_image(&out_image, &t->out.shm, out_format, w, h);
212 	test_init_image(&ref_image, &t->ref.shm, ref_format, w, h);
213 
214 	die_unless(out_image.depth == ref_image.depth);
215 	die_unless(out_image.bits_per_pixel == ref_image.bits_per_pixel);
216 	die_unless(out_image.bits_per_pixel == 32);
217 
218 	XShmGetImage(t->out.dpy, out_draw, &out_image, x, y, AllPlanes);
219 	out = (uint32_t *)out_image.data;
220 
221 	XShmGetImage(t->ref.dpy, ref_draw, &ref_image, x, y, AllPlanes);
222 	ref = (uint32_t *)ref_image.data;
223 
224 	/* Start with an exact comparison. However, one quicky desires
225 	 * a fuzzy comparator to hide hardware inaccuracies...
226 	 */
227 	mask = depth_mask(out_image.depth);
228 	for (j = 0; j < h; j++) {
229 		for (i = 0; i < w; i++) {
230 			uint32_t a = out[i] & mask;
231 			uint32_t b = ref[i] & mask;
232 			if (a != b && pixel_difference(a, b) > MAX_DELTA) {
233 				show_pixels(buf,
234 					    &out_image, &ref_image,
235 					    i, j, w, h);
236 				save_image(&out_image, "out.png");
237 				save_image(&ref_image,  "ref.png");
238 				die("discrepancy found at (%d+%d, %d+%d): found %08x, expected %08x (delta: %d)\n%s%s\n",
239 				    x,i, y,j, a, b, pixel_difference(a, b), buf, info);
240 			}
241 		}
242 		out = (uint32_t *)((char *)out + out_image.bytes_per_line);
243 		ref = (uint32_t *)((char *)ref + ref_image.bytes_per_line);
244 	}
245 }
246 
247 static int
_native_byte_order_lsb(void)248 _native_byte_order_lsb(void)
249 {
250 	int x = 1;
251 	return *((char *) &x) == 1;
252 }
253 
254 void
test_init_image(XImage * ximage,XShmSegmentInfo * shm,XRenderPictFormat * format,int width,int height)255 test_init_image(XImage *ximage,
256 		XShmSegmentInfo *shm,
257 		XRenderPictFormat *format,
258 		int width, int height)
259 {
260 	int native_byte_order = _native_byte_order_lsb() ? LSBFirst : MSBFirst;
261 
262 	ximage->width = width;
263 	ximage->height = height;
264 	ximage->format = ZPixmap;
265 	ximage->data = shm->shmaddr;
266 	ximage->obdata = (void *)shm;
267 	ximage->byte_order = native_byte_order;
268 	ximage->bitmap_unit = 32;
269 	ximage->bitmap_bit_order = native_byte_order;
270 	ximage->bitmap_pad = 32;
271 	ximage->depth = format->depth;
272 	ximage->bytes_per_line = 4*width;
273 	ximage->bits_per_pixel = 32;
274 	ximage->red_mask = 0xff << 16;
275 	ximage->green_mask = 0xff << 8;
276 	ximage->blue_mask = 0xff << 0;
277 	ximage->xoffset = 0;
278 
279 	XInitImage(ximage);
280 }
281