1#include <stdint.h>
2#include <stdio.h>
3#include <stdlib.h>
4
5#include <X11/Xutil.h> /* for XDestroyImage */
6#include <pixman.h> /* for pixman blt functions */
7
8#include "test.h"
9
10enum trapezoid {
11	RECT_ALIGN,
12	RECT_UNALIGN,
13	GENERAL
14};
15
16static const uint8_t ops[] = {
17	PictOpClear,
18	PictOpSrc,
19	PictOpDst,
20};
21
22static XRenderPictFormat *mask_format(Display *dpy, enum mask mask)
23{
24	switch (mask) {
25	default:
26	case MASK_NONE:
27	case MASK_NONE_AA:
28	       	return NULL;
29	case MASK_A1:
30	       	return  XRenderFindStandardFormat(dpy, PictStandardA1);
31	case MASK_A8:
32	       	return  XRenderFindStandardFormat(dpy, PictStandardA8);
33	}
34}
35
36static const char *mask_name(enum mask mask)
37{
38	switch (mask) {
39	default:
40	case MASK_NONE: return "none";
41	case MASK_NONE_AA: return "none/aa";
42	case MASK_A1: return "a1";
43	case MASK_A8: return "a8";
44	}
45}
46
47static const char *trapezoid_name(enum trapezoid trapezoid)
48{
49	switch (trapezoid) {
50	default:
51	case RECT_ALIGN: return "pixel-aligned";
52	case RECT_UNALIGN: return "rectilinear";
53	case GENERAL: return "general";
54	}
55}
56
57static void
58show_cells(char *buf,
59	   const uint32_t *out, const uint32_t *ref,
60	   int x, int y, int w, int h)
61{
62	int i, j, len = 0;
63
64	for (j = y - 2; j <= y + 2; j++) {
65		if (j < 0 || j >= h)
66			continue;
67
68		for (i = x - 2; i <= x + 2; i++) {
69			if (i < 0 || i >= w)
70				continue;
71
72			len += sprintf(buf+len, "%08x ", out[j*w+i]);
73		}
74
75		len += sprintf(buf+len, "\t");
76
77		for (i = x - 2; i <= x + 2; i++) {
78			if (i < 0 || i >= w)
79				continue;
80
81			len += sprintf(buf+len, "%08x ", ref[j*w+i]);
82		}
83
84		len += sprintf(buf+len, "\n");
85	}
86}
87
88
89static void fill_rect(struct test_display *t, Picture p, XRenderPictFormat *format,
90		      uint8_t op, int x, int y, int w, int h,
91		      int dx, int dy, enum mask mask,
92		      int use_window, int tx, int ty,
93		      uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
94{
95	XRenderColor color;
96	XTrapezoid trap;
97	Drawable tmp;
98	Picture src;
99	int w1 = w + (dx!=0);
100	int h1 = h + (dy!=0);
101
102	if (use_window) {
103		XSetWindowAttributes attr;
104
105		attr.override_redirect = 1;
106		tmp = XCreateWindow(t->dpy, DefaultRootWindow(t->dpy),
107				    tx, ty,
108				    w1, h1,
109				    0, format->depth,
110				    InputOutput,
111				    DefaultVisual(t->dpy,
112						  DefaultScreen(t->dpy)),
113				    CWOverrideRedirect, &attr);
114		XMapWindow(t->dpy, tmp);
115	} else
116		tmp = XCreatePixmap(t->dpy, DefaultRootWindow(t->dpy),
117				    w1, h1, format->depth);
118
119	src = XRenderCreatePicture(t->dpy, tmp, format, 0, NULL);
120	color.red = red * alpha;
121	color.green = green * alpha;
122	color.blue = blue * alpha;
123	color.alpha = alpha << 8 | alpha;
124	XRenderFillRectangle(t->dpy, PictOpSrc, src, &color, 0, 0, w1, h1);
125
126	trap.left.p1.x = trap.left.p2.x = (x << 16) + dx;
127	trap.top = trap.left.p1.y = trap.right.p1.y = (y << 16) + dy;
128	trap.right.p1.x = trap.right.p2.x = ((x + w) << 16) + dx;
129	trap.bottom = trap.left.p2.y = trap.right.p2.y = ((y + h) << 16) + dy;
130
131	XRenderCompositeTrapezoids(t->dpy,
132				   op, src, p, mask_format(t->dpy, mask),
133				   0, 0, &trap, 1);
134
135	XRenderFreePicture(t->dpy, src);
136	if (use_window)
137		XDestroyWindow(t->dpy, tmp);
138	else
139		XFreePixmap(t->dpy, tmp);
140}
141
142static void pixel_tests(struct test *t, int reps, int sets, enum target target, int use_window)
143{
144	struct test_target tt;
145	XImage image;
146	uint32_t *cells = malloc(t->out.width*t->out.height*4);
147	struct {
148		uint16_t x, y;
149	} *pixels = malloc(reps*sizeof(*pixels));
150	int r, s;
151
152	printf("Testing setting of single pixels (%s using %s): ",
153	       test_target_name(target),
154	       use_window ? "window" : "pixmap");
155	fflush(stdout);
156
157	test_target_create_render(&t->out, target, &tt);
158
159	for (s = 0; s < sets; s++) {
160		for (r = 0; r < reps; r++) {
161			int x = rand() % (tt.width - 1);
162			int y = rand() % (tt.height - 1);
163			int red = rand() % 0xff;
164			int green = rand() % 0xff;
165			int blue = rand() % 0xff;
166			int alpha = rand() % 0xff;
167
168			int tx, ty;
169
170			do {
171				tx = rand() % (tt.width - 1);
172				ty = rand() % (tt.height - 1);
173			} while (tx == x && ty == y);
174
175			fill_rect(&t->out, tt.picture,
176				  use_window ? t->out.format : tt.format,
177				  PictOpSrc, x, y, 1, 1,
178				  0, 0, MASK_NONE,
179				  use_window, tx, ty,
180				  red, green, blue, alpha);
181
182			pixels[r].x = x;
183			pixels[r].y = y;
184			cells[y*t->out.width+x] = color(red, green, blue, alpha);
185		}
186
187		test_init_image(&image, &t->out.shm, tt.format, 1, 1);
188
189		for (r = 0; r < reps; r++) {
190			uint32_t result;
191			uint32_t x = pixels[r].x;
192			uint32_t y = pixels[r].y;
193
194			XShmGetImage(t->out.dpy, tt.draw, &image,
195				     x, y, AllPlanes);
196
197			result = *(uint32_t *)image.data;
198			if (!pixel_equal(image.depth, result,
199					 cells[y*tt.width+x])) {
200				uint32_t mask = depth_mask(image.depth);
201				die("failed to set pixel (%d,%d) to %08x [%08x], found %08x [%08x] instead\n",
202				    x, y,
203				    cells[y*tt.width+x] & mask,
204				    cells[y*tt.width+x],
205				    result & mask,
206				    result);
207			}
208		}
209	}
210	printf("passed [%d iterations x %d]\n", reps, sets);
211
212	test_target_destroy_render(&t->out, &tt);
213
214	free(pixels);
215	free(cells);
216}
217
218static void clear(struct test_display *dpy, struct test_target *tt)
219{
220	XRenderColor render_color = {0};
221	XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, &render_color,
222			     0, 0, tt->width, tt->height);
223}
224
225static void set_mask(struct test_display *t, struct test_target *tt, enum mask mask)
226{
227	XRenderPictureAttributes pa;
228
229	switch (mask) {
230	case MASK_NONE:
231		pa.poly_edge = PolyEdgeSharp;
232		break;
233	default:
234		pa.poly_edge = PolyEdgeSmooth;
235		break;
236	}
237
238	XRenderChangePicture(t->dpy, tt->picture, CPPolyEdge, &pa);
239}
240
241static void fill(uint32_t *cells,
242		 int x, int y,
243		 int w, int h,
244		 int max_width, int max_height,
245		 uint32_t pixel)
246{
247	if (x < 0)
248		w += x, x = 0;
249	if (y < 0)
250		h += y, y = 0;
251	if (x >= max_width || y >= max_height)
252		return;
253
254	if (x + w > max_width)
255		w = max_width - x;
256	if (y + h > max_height)
257		h = max_height - y;
258	if (w <= 0 || h <= 0)
259		return;
260
261	pixman_fill(cells, max_width, 32, x, y, w, h, pixel);
262}
263
264static void area_tests(struct test *t, int reps, int sets, enum target target, int use_window)
265{
266	struct test_target tt;
267	XImage image;
268	uint32_t *cells = calloc(sizeof(uint32_t), t->out.width*t->out.height);
269	int r, s, x, y;
270
271	printf("Testing area sets (%s using %s source): ",
272	       test_target_name(target),
273	       use_window ? "window" : "pixmap");
274	fflush(stdout);
275
276	test_target_create_render(&t->out, target, &tt);
277	clear(&t->out, &tt);
278
279	test_init_image(&image, &t->out.shm, tt.format, tt.width, tt.height);
280
281	for (s = 0; s < sets; s++) {
282		for (r = 0; r < reps; r++) {
283			int red = rand() % 0xff;
284			int green = rand() % 0xff;
285			int blue = rand() % 0xff;
286			int alpha = rand() % 0xff;
287			int tx, ty, try = 50;
288			int w, h;
289
290			x = rand() % (2*tt.width) - tt.width;
291			y = rand() % (2*tt.height) - tt.height;
292			if (use_window) {
293				do {
294					w = 1 + rand() % (tt.width - 1);
295					h = 1 + rand() % (tt.height - 1);
296
297					tx = w == tt.width ? 0 : rand() % (tt.width - w);
298					ty = h == tt.height ? 0 : rand() % (tt.height - h);
299				} while (((tx+w > x && tx < x+w) &&
300					  (ty+h > y && ty < y+h)) &&
301					 --try);
302
303				if (!try)
304					continue;
305			} else {
306				w = 1 + rand() % (2*tt.width);
307				h = 1 + rand() % (2*tt.height);
308				tx = ty = 0;
309			}
310
311			fill_rect(&t->out, tt.picture,
312				  use_window ? t->out.format : tt.format,
313				  PictOpSrc, x, y, w, h,
314				  0, 0, MASK_NONE,
315				  use_window, tx, ty,
316				  red, green, blue, alpha);
317
318			if (use_window)
319				fill(cells, tx, ty, w, h, tt.width, tt.height,
320				     color(red, green, blue, alpha));
321			fill(cells, x, y, w, h, tt.width, tt.height,
322			     color(red, green, blue, alpha));
323
324		}
325
326		XShmGetImage(t->out.dpy, tt.draw, &image, 0, 0, AllPlanes);
327
328		for (y = 0; y < tt.height; y++) {
329			for (x = 0; x < tt.width; x++) {
330				uint32_t result =
331					*(uint32_t *)(image.data +
332						      y*image.bytes_per_line +
333						      image.bits_per_pixel*x/8);
334				if (!pixel_equal(image.depth, result, cells[y*tt.width+x])) {
335					char buf[600];
336					uint32_t mask = depth_mask(image.depth);
337					show_cells(buf,
338						   (uint32_t*)image.data, cells,
339						   x, y, tt.width, tt.height);
340
341					die("failed to set pixel (%d,%d) to %08x [%08x], found %08x [%08x] instead\n%s",
342					    x, y,
343					    cells[y*tt.width+x] & mask,
344					    cells[y*tt.width+x],
345					    result & mask,
346					    result, buf);
347				}
348			}
349		}
350	}
351
352	printf("passed [%d iterations x %d]\n", reps, sets);
353
354	test_target_destroy_render(&t->out, &tt);
355	free(cells);
356}
357
358static void rect_tests(struct test *t,
359		       int dx, int dy,
360		       enum mask mask,
361		       int reps, int sets,
362		       enum target target,
363		       int use_window)
364{
365	struct test_target out, ref;
366	int r, s;
367
368	printf("Testing area fills (offset %dx%d, mask %s) (%s using %s source): ",
369	       dx, dy, mask_name(mask), test_target_name(target),
370	       use_window ? "window" : "pixmap");
371	fflush(stdout);
372
373	test_target_create_render(&t->out, target, &out);
374	clear(&t->out, &out);
375	set_mask(&t->out, &out, mask);
376
377	test_target_create_render(&t->ref, target, &ref);
378	clear(&t->ref, &ref);
379	set_mask(&t->ref, &ref, mask);
380
381	for (s = 0; s < sets; s++) {
382		for (r = 0; r < reps; r++) {
383			int x, y, w, h;
384			int op = ops[rand() % sizeof(ops)];
385			int red = rand() % 0xff;
386			int green = rand() % 0xff;
387			int blue = rand() % 0xff;
388			int alpha = rand() % 0xff;
389			int tx, ty, try = 50;
390
391			do {
392				x = rand() % (out.width - 1);
393				y = rand() % (out.height - 1);
394				w = 1 + rand() % (out.width - x - 1);
395				h = 1 + rand() % (out.height - y - 1);
396				tx = w == out.width ? 0 : rand() % (out.width - w);
397				ty = h == out.height ? 0 : rand() % (out.height - h);
398			} while (((tx+w > x && tx < x+w) &&
399				  (ty+h > y && ty < y+h)) &&
400				 --try);
401
402			if (try) {
403				fill_rect(&t->out, out.picture,
404					  use_window ? t->out.format : out.format,
405					  op, x, y, w, h,
406					  dx, dy, mask,
407					  use_window, tx, ty,
408					  red, green, blue, alpha);
409				fill_rect(&t->ref, ref.picture,
410					  use_window ? t->ref.format : ref.format,
411					  op, x, y, w, h,
412					  dx, dy, mask,
413					  use_window, tx, ty,
414					  red, green, blue, alpha);
415			}
416		}
417
418		test_compare(t,
419			     out.draw, out.format,
420			     ref.draw, ref.format,
421			     0, 0, out.width, out.height,
422			     "");
423	}
424
425	printf("passed [%d iterations x %d]\n", reps, sets);
426
427	test_target_destroy_render(&t->out, &out);
428	test_target_destroy_render(&t->ref, &ref);
429}
430
431static void random_trapezoid(XTrapezoid *trap, enum trapezoid trapezoid,
432			     int x1, int y1, int x2, int y2)
433{
434	switch (trapezoid) {
435	case RECT_ALIGN:
436		x1 = x1 + rand() % (x2 - x1);
437		x2 = x1 + rand() % (x2 - x1);
438		y1 = y1 + rand() % (y2 - y1);
439		y2 = y1 + rand() % (y2 - y1);
440
441		trap->left.p1.x = trap->left.p2.x = x1 << 16;
442		trap->top = trap->left.p1.y = trap->right.p1.y = y1 << 16;
443		trap->right.p1.x = trap->right.p2.x = x2 << 16;
444		trap->bottom = trap->left.p2.y = trap->right.p2.y = y2 << 16;
445		break;
446
447	case RECT_UNALIGN:
448		x1 <<= 16; x2 <<= 16;
449		y1 <<= 16; y2 <<= 16;
450
451		x1 = x1 + rand() % (x2 - x1);
452		x2 = x1 + rand() % (x2 - x1);
453		y1 = y1 + rand() % (y2 - y1);
454		y2 = y1 + rand() % (y2 - y1);
455
456		trap->left.p1.x = trap->left.p2.x = x1;
457		trap->top = trap->left.p1.y = trap->right.p1.y = y1;
458		trap->right.p1.x = trap->right.p2.x = x2;
459		trap->bottom = trap->left.p2.y = trap->right.p2.y = y2;
460		break;
461
462	case GENERAL:
463		x1 <<= 16; x2 <<= 16;
464		y1 <<= 16; y2 <<= 16;
465
466		trap->top = y1 + rand() % (y2 - y1);
467		trap->bottom = y1 + rand() % (y2 - y1);
468
469		trap->left.p1.x = x1 + rand() % (x2 - x1);
470		trap->left.p2.x = x1 + rand() % (x2 - x1);
471
472		trap->right.p1.x = x1 + rand() % (x2 - x1);
473		trap->right.p2.x = x1 + rand() % (x2 - x1);
474		break;
475	}
476}
477
478static void fill_traps(struct test_display *t, Picture p, XRenderPictFormat *format,
479		       uint8_t op, XTrapezoid *traps, int ntraps, enum mask mask,
480		       int srcx, int srcy, int srcw, int srch,
481		       uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
482{
483	XRenderColor color;
484	Drawable tmp;
485	Picture src;
486
487	tmp = XCreatePixmap(t->dpy, DefaultRootWindow(t->dpy),
488			    srcw, srch, format->depth);
489
490	src = XRenderCreatePicture(t->dpy, tmp, format, 0, NULL);
491	color.red = red * alpha;
492	color.green = green * alpha;
493	color.blue = blue * alpha;
494	color.alpha = alpha << 8 | alpha;
495	XRenderFillRectangle(t->dpy, PictOpSrc, src, &color, 0, 0, srcw, srch);
496
497	XRenderCompositeTrapezoids(t->dpy,
498				   op, src, p, mask_format(t->dpy, mask),
499				   srcx, srcy, traps, ntraps);
500
501	XRenderFreePicture(t->dpy, src);
502	XFreePixmap(t->dpy, tmp);
503}
504
505static void trap_tests(struct test *t,
506		       enum mask mask,
507		       enum trapezoid trapezoid,
508		       int reps, int sets,
509		       enum target target)
510{
511	struct test_target out, ref;
512	XTrapezoid *traps;
513	int max_traps = 65536;
514	int r, s, n;
515
516	traps = malloc(sizeof(*traps) * max_traps);
517	if (traps == NULL)
518		return;
519
520	printf("Testing trapezoids (%s with mask %s) (%s): ",
521	       trapezoid_name(trapezoid),
522	       mask_name(mask),
523	       test_target_name(target));
524	fflush(stdout);
525
526	test_target_create_render(&t->out, target, &out);
527	clear(&t->out, &out);
528	set_mask(&t->out, &out, mask);
529
530	test_target_create_render(&t->ref, target, &ref);
531	clear(&t->ref, &ref);
532	set_mask(&t->ref, &ref, mask);
533
534	for (s = 0; s < sets; s++) {
535		for (r = 0; r < reps; r++) {
536			int op = ops[rand() % sizeof(ops)];
537			int red = rand() % 0xff;
538			int green = rand() % 0xff;
539			int blue = rand() % 0xff;
540			int alpha = rand() % 0xff;
541			int num_traps = rand() % max_traps;
542			int srcx = rand() % 2*out.width - out.width;
543			int srcy = rand() % 2*out.height - out.height;
544			int srcw = rand() % out.width;
545			int srch = rand() % out.height;
546
547			for (n = 0; n < num_traps; n++)
548				random_trapezoid(&traps[n], 0,
549						 0, 0, out.width, out.height);
550
551
552			fill_traps(&t->out, out.picture, out.format,
553				   op, traps, num_traps, mask,
554				   srcx, srcy, srcw, srch,
555				   red, green, blue, alpha);
556
557			fill_traps(&t->ref, ref.picture, ref.format,
558				   op, traps, num_traps, mask,
559				   srcx, srcy, srcw, srch,
560				   red, green, blue, alpha);
561		}
562
563		test_compare(t,
564			     out.draw, out.format,
565			     ref.draw, ref.format,
566			     0, 0, out.width, out.height,
567			     "");
568	}
569
570	printf("passed [%d iterations x %d]\n", reps, sets);
571
572	test_target_destroy_render(&t->out, &out);
573	test_target_destroy_render(&t->ref, &ref);
574	free(traps);
575}
576
577int main(int argc, char **argv)
578{
579	struct test test;
580	int i, dx, dy;
581	enum target target;
582	enum mask mask;
583	enum trapezoid trapezoid;
584
585	test_init(&test, argc, argv);
586
587	for (i = 0; i <= DEFAULT_ITERATIONS; i++) {
588		int reps = REPS(i), sets = SETS(i);
589
590		for (target = TARGET_FIRST; target <= TARGET_LAST; target++) {
591			pixel_tests(&test, reps, sets, target, 0);
592			area_tests(&test, reps, sets, target, 0);
593			for (dy = 0; dy < 1 << 16; dy += 1 << 14)
594				for (dx = 0; dx < 1 << 16; dx += 1 << 14)
595					for (mask = MASK_NONE; mask <= MASK_A8; mask++)
596						rect_tests(&test, dx, dy, mask, reps, sets, target, 0);
597			if (target != CHILD) {
598				pixel_tests(&test, reps, sets, target, 1);
599				area_tests(&test, reps, sets, target, 1);
600				for (dy = 0; dy < 1 << 16; dy += 1 << 14)
601					for (dx = 0; dx < 1 << 16; dx += 1 << 14)
602						for (mask = MASK_NONE; mask <= MASK_A8; mask++)
603							rect_tests(&test, dx, dy, mask, reps, sets, target, 1);
604			}
605		}
606
607		for (target = TARGET_FIRST; target <= TARGET_LAST; target++)
608			for (trapezoid = RECT_ALIGN; trapezoid <= GENERAL; trapezoid++)
609				trap_tests(&test, mask, trapezoid, reps, sets, target);
610	}
611
612	return 0;
613}
614