1#include <stdint.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <stdbool.h>
5#include <stdarg.h>
6#include <string.h>
7
8#include <X11/Xutil.h> /* for XDestroyImage */
9#include <pixman.h> /* for pixman blt functions */
10
11#include "test.h"
12
13static const XRenderColor colors[] = {
14	/* red, green, blue, alpha */
15	{ 0 },
16	{ 0, 0, 0, 0xffff },
17	{ 0xffff, 0, 0, 0xffff },
18	{ 0, 0xffff, 0, 0xffff },
19	{ 0, 0, 0xffff, 0xffff },
20	{ 0xffff, 0xffff, 0xffff, 0xffff },
21};
22
23static struct clip {
24	void *func;
25} clips[] = {
26	{ NULL },
27};
28
29static int _x_error_occurred;
30
31static int
32_check_error_handler(Display     *display,
33		     XErrorEvent *event)
34{
35	_x_error_occurred = 1;
36	return False; /* ignored */
37}
38
39static void clear(struct test_display *dpy,
40		  struct test_target *tt,
41		  const XRenderColor *c)
42{
43	XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, c,
44			     0, 0, tt->width, tt->height);
45}
46
47static bool check_op(struct test_display *dpy, int op, struct test_target *tt)
48{
49	XRenderColor render_color = {0};
50
51	XSync(dpy->dpy, True);
52	_x_error_occurred = 0;
53
54	XRenderFillRectangle(dpy->dpy, op,
55			     tt->picture, &render_color,
56			     0, 0, 0, 0);
57
58	XSync(dpy->dpy, True);
59	return _x_error_occurred == 0;
60}
61
62struct glyph_iter {
63	enum {
64		GLYPHS, OP, DST, SRC, MASK, CLIP,
65	} stage;
66
67	int glyph_format;
68	int op;
69	int dst_color;
70	int src_color;
71	int mask_format;
72	int clip;
73
74	struct {
75		struct test_display *dpy;
76		struct test_target tt;
77		GlyphSet glyphset;
78		Picture src;
79		XRenderPictFormat *mask_format;
80	} ref, out;
81};
82
83static void glyph_iter_init(struct glyph_iter *gi,
84			    struct test *t, enum target target)
85{
86	memset(gi, 0, sizeof(*gi));
87
88	gi->out.dpy = &t->out;
89	test_target_create_render(&t->out, target, &gi->out.tt);
90
91	gi->ref.dpy = &t->ref;
92	test_target_create_render(&t->ref, target, &gi->ref.tt);
93
94	gi->stage = GLYPHS;
95	gi->glyph_format = -1;
96	gi->op = -1;
97	gi->dst_color = -1;
98	gi->src_color = -1;
99	gi->mask_format = -1;
100	gi->clip = -1;
101}
102
103static void render_clear(char *image, int image_size, int bpp)
104{
105	memset(image, 0, image_size);
106}
107
108static void render_black(char *image, int image_size, int bpp)
109{
110	if (bpp == 4) {
111		uint32_t *p = (uint32_t *)image;
112		image_size /= 4;
113		while (image_size--)
114			*p++ = 0x000000ff;
115	} else
116		memset(image, 0x55, image_size);
117}
118
119static void render_green(char *image, int image_size, int bpp)
120{
121	if (bpp == 4) {
122		uint32_t *p = (uint32_t *)image;
123		image_size /= 4;
124		while (image_size--)
125			*p++ = 0xffff0000;
126	} else
127		memset(image, 0xaa, image_size);
128}
129
130static void render_white(char *image, int image_size, int bpp)
131{
132	memset(image, 0xff, image_size);
133}
134
135static GlyphSet create_glyphs(Display *dpy, int format_id)
136{
137#define N_GLYPHS 4
138	XRenderPictFormat *format;
139	XGlyphInfo glyph = { 8, 8, 0, 0, 8, 0 };
140	char image[4*8*8];
141	GlyphSet glyphset;
142	Glyph gid;
143	int image_size;
144	int bpp;
145	int n;
146
147	format = XRenderFindStandardFormat(dpy, format_id);
148	if (format == NULL)
149		return 0;
150
151	switch (format_id) {
152	case PictStandardARGB32:
153	case PictStandardRGB24:
154		image_size = 4 * 8 * 8;
155		bpp = 4;
156		break;
157	case PictStandardA8:
158	case PictStandardA4:
159		image_size = 8 * 8;
160		bpp = 1;
161		break;
162	case PictStandardA1:
163		image_size = 8;
164		bpp = 0;
165		break;
166	default:
167		return 0;
168	}
169
170	glyphset = XRenderCreateGlyphSet(dpy, format);
171	for (n = 0; n < N_GLYPHS; n++) {
172		gid = n;
173
174		switch (n) {
175		case 0: render_clear(image, image_size, bpp); break;
176		case 1: render_black(image, image_size, bpp); break;
177		case 2: render_green(image, image_size, bpp); break;
178		case 3: render_white(image, image_size, bpp); break;
179		}
180
181		XRenderAddGlyphs(dpy, glyphset,
182				 &gid, &glyph, 1, image, image_size);
183	}
184
185	return glyphset;
186}
187
188static const char *glyph_name(int n)
189{
190	switch (n) {
191	case 0: return "clear";
192	case 1: return "black";
193	case 2: return "green";
194	case 3: return "white";
195	default: return "unknown";
196	}
197}
198
199static bool glyph_iter_next(struct glyph_iter *gi)
200{
201restart:
202	if (gi->stage == GLYPHS) {
203		if (++gi->glyph_format == PictStandardNUM)
204			return false;
205
206		if (gi->out.glyphset)
207			XRenderFreeGlyphSet(gi->out.dpy->dpy,
208					    gi->out.glyphset);
209		gi->out.glyphset = create_glyphs(gi->out.dpy->dpy,
210					       gi->glyph_format);
211
212		if (gi->ref.glyphset)
213			XRenderFreeGlyphSet(gi->ref.dpy->dpy,
214					    gi->ref.glyphset);
215		gi->ref.glyphset = create_glyphs(gi->ref.dpy->dpy,
216					       gi->glyph_format);
217
218		gi->stage++;
219	}
220
221	if (gi->stage == OP) {
222		do {
223			if (++gi->op == 255)
224				goto reset_op;
225		} while (!check_op(gi->out.dpy, gi->op, &gi->out.tt) ||
226			 !check_op(gi->ref.dpy, gi->op, &gi->ref.tt));
227
228		gi->stage++;
229	}
230
231	if (gi->stage == DST) {
232		if (++gi->dst_color == ARRAY_SIZE(colors))
233			goto reset_dst;
234
235		gi->stage++;
236	}
237
238	if (gi->stage == SRC) {
239		if (++gi->src_color == ARRAY_SIZE(colors))
240			goto reset_src;
241
242		if (gi->ref.src)
243			XRenderFreePicture(gi->ref.dpy->dpy, gi->ref.src);
244		gi->ref.src = XRenderCreateSolidFill(gi->ref.dpy->dpy,
245						     &colors[gi->src_color]);
246
247		if (gi->out.src)
248			XRenderFreePicture(gi->out.dpy->dpy, gi->out.src);
249		gi->out.src = XRenderCreateSolidFill(gi->out.dpy->dpy,
250						     &colors[gi->src_color]);
251
252		gi->stage++;
253	}
254
255	if (gi->stage == MASK) {
256		if (++gi->mask_format > PictStandardNUM)
257			goto reset_mask;
258
259		if (gi->mask_format == PictStandardRGB24)
260			gi->mask_format++;
261
262		if (gi->mask_format < PictStandardNUM) {
263			gi->out.mask_format = XRenderFindStandardFormat(gi->out.dpy->dpy,
264									gi->mask_format);
265			gi->ref.mask_format = XRenderFindStandardFormat(gi->ref.dpy->dpy,
266									gi->mask_format);
267		} else {
268			gi->out.mask_format = NULL;
269			gi->ref.mask_format = NULL;
270		}
271
272		gi->stage++;
273	}
274
275	if (gi->stage == CLIP) {
276		if (++gi->clip == ARRAY_SIZE(clips))
277			goto reset_clip;
278
279		gi->stage++;
280	}
281
282	gi->stage--;
283	return true;
284
285reset_op:
286	gi->op = -1;
287reset_dst:
288	gi->dst_color = -1;
289reset_src:
290	gi->src_color = -1;
291reset_mask:
292	gi->mask_format = -1;
293reset_clip:
294	gi->clip = -1;
295	gi->stage--;
296	goto restart;
297}
298
299static void glyph_iter_fini(struct glyph_iter *gi)
300{
301	if (gi->out.glyphset)
302		XRenderFreeGlyphSet (gi->out.dpy->dpy, gi->out.glyphset);
303	if (gi->ref.glyphset)
304		XRenderFreeGlyphSet (gi->ref.dpy->dpy, gi->ref.glyphset);
305
306	test_target_destroy_render(gi->out.dpy, &gi->out.tt);
307	test_target_destroy_render(gi->ref.dpy, &gi->ref.tt);
308}
309
310static const char *stdformat_to_str(int id)
311{
312	switch (id) {
313	case PictStandardARGB32: return "ARGB32";
314	case PictStandardRGB24: return "RGB24";
315	case PictStandardA8: return "A8";
316	case PictStandardA4: return "A4";
317	case PictStandardA1: return "A1";
318	default: return "none";
319	}
320}
321
322static char *glyph_iter_to_string(struct glyph_iter *gi,
323				  const char *format,
324				  ...)
325{
326	static char buf[100];
327	va_list ap;
328	int len;
329
330	len = sprintf(buf, "glyphs=%s, op=%d, dst=%08x, src=%08x, mask=%s",
331		      stdformat_to_str(gi->glyph_format), gi->op,
332		      xrender_color(&colors[gi->dst_color]),
333		      xrender_color(&colors[gi->src_color]),
334		      stdformat_to_str(gi->mask_format));
335
336	if (format) {
337		buf[len++] = ' ';
338		va_start(ap, format);
339		vsprintf(buf+len, format, ap);
340		va_end(ap);
341	}
342
343	return buf;
344}
345
346static void single(struct test *t, enum target target)
347{
348	struct glyph_iter gi;
349	int n;
350
351	printf("Testing single glyph (%s): ", test_target_name(target));
352	fflush(stdout);
353
354	glyph_iter_init(&gi, t, target);
355	while (glyph_iter_next(&gi)) {
356		XGlyphElt8 elt;
357		char id[N_GLYPHS];
358
359		for (n = 0; n < N_GLYPHS; n++) {
360			id[n] = n;
361
362			elt.chars = &id[n];
363			elt.nchars = 1;
364			elt.xOff = 0;
365			elt.yOff = 0;
366
367			clear(gi.out.dpy, &gi.out.tt, &colors[gi.dst_color]);
368			elt.glyphset = gi.out.glyphset;
369			XRenderCompositeText8 (gi.out.dpy->dpy, gi.op,
370					       gi.out.src,
371					       gi.out.tt.picture,
372					       gi.out.mask_format,
373					       0, 0,
374					       0, 8,
375					       &elt, 1);
376
377			clear(gi.ref.dpy, &gi.ref.tt, &colors[gi.dst_color]);
378			elt.glyphset = gi.ref.glyphset;
379			XRenderCompositeText8 (gi.ref.dpy->dpy, gi.op,
380					       gi.ref.src,
381					       gi.ref.tt.picture,
382					       gi.ref.mask_format,
383					       0, 0,
384					       0, 8,
385					       &elt, 1);
386			test_compare(t,
387				     gi.out.tt.draw, gi.out.tt.format,
388				     gi.ref.tt.draw, gi.ref.tt.format,
389				     0, 0, gi.out.tt.width, gi.out.tt.height,
390				     glyph_iter_to_string(&gi,
391							  "glyph=%s",
392							  glyph_name(n)));
393		}
394
395		elt.chars = &id[0];
396		elt.nchars = n;
397		clear(gi.out.dpy, &gi.out.tt, &colors[gi.dst_color]);
398		elt.glyphset = gi.out.glyphset;
399		XRenderCompositeText8 (gi.out.dpy->dpy, gi.op,
400				       gi.out.src,
401				       gi.out.tt.picture,
402				       gi.out.mask_format,
403				       0, 0,
404				       0, 8,
405				       &elt, 1);
406
407		clear(gi.ref.dpy, &gi.ref.tt, &colors[gi.dst_color]);
408		elt.glyphset = gi.ref.glyphset;
409		XRenderCompositeText8 (gi.ref.dpy->dpy, gi.op,
410				       gi.ref.src,
411				       gi.ref.tt.picture,
412				       gi.ref.mask_format,
413				       0, 0,
414				       0, 8,
415				       &elt, 1);
416		test_compare(t,
417			     gi.out.tt.draw, gi.out.tt.format,
418			     gi.ref.tt.draw, gi.ref.tt.format,
419			     0, 0, gi.out.tt.width, gi.out.tt.height,
420			     glyph_iter_to_string(&gi, "all"));
421	}
422	glyph_iter_fini(&gi);
423}
424
425int main(int argc, char **argv)
426{
427	struct test test;
428	int t;
429
430	test_init(&test, argc, argv);
431	XSetErrorHandler(_check_error_handler);
432
433	for (t = TARGET_FIRST; t <= TARGET_LAST; t++) {
434		single(&test, t);
435		//overlapping(&test, t);
436		//gap(&test, t);
437		//mixed(&test, t);
438	}
439
440	return 0;
441}
442