1
/*
2
 * Copyright © 2005 Red Hat, Inc.
3
 *
4
 * Permission to use, copy, modify, distribute, and sell this software
5
 * and its documentation for any purpose is hereby granted without
6
 * fee, provided that the above copyright notice appear in all copies
7
 * and that both that copyright notice and this permission notice
8
 * appear in supporting documentation, and that the name of
9
 * Red Hat, Inc. not be used in advertising or publicity pertaining to
10
 * distribution of the software without specific, written prior
11
 * permission. Red Hat, Inc. makes no representations about the
12
 * suitability of this software for any purpose.  It is provided "as
13
 * is" without express or implied warranty.
14
 *
15
 * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17
 * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
18
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
 *
23
 * Author: Carl D. Worth <cworth@cworth.org>
24
 */
25

            
26
#include "cairo-test.h"
27

            
28
#include <stdio.h>
29
#include <stdlib.h>
30

            
31
#include "cairo.h"
32
#include "cairo-xlib.h"
33

            
34
#include "cairo-boilerplate-xlib.h"
35

            
36
#include "buffer-diff.h"
37

            
38
#define SIZE 100
39
#define OFFSCREEN_OFFSET 50
40

            
41
cairo_bool_t result = 0;
42

            
43
#if CAIRO_HAS_XLIB_XRENDER_SURFACE
44

            
45
#include "cairo-xlib-xrender.h"
46

            
47
/* Vladimir Vukicevic reported that surfaces were being created with
48
 * mismatching Visuals and XRenderPictFormats.
49
 */
50
static cairo_bool_t
51
surface_compare_visual_and_format (cairo_surface_t *surface)
52
{
53
    Display *dpy;
54
    Visual *visual;
55
    XRenderPictFormat *format;
56

            
57
    dpy = cairo_xlib_surface_get_display (surface);
58

            
59
    visual = cairo_xlib_surface_get_visual (surface);
60
    if (visual == NULL)
61
	return TRUE;
62

            
63
    format = cairo_xlib_surface_get_xrender_format (surface);
64
    if (format == NULL)
65
	return TRUE;
66

            
67
    return format == XRenderFindVisualFormat (dpy, visual);
68

            
69
}
70
#else
71

            
72
static cairo_bool_t
73
surface_compare_visual_and_format (cairo_surface_t *surface)
74
{
75
    return TRUE;
76
}
77

            
78
#endif
79

            
80
static cairo_bool_t
81
check_similar_visual_and_format (cairo_surface_t *surface)
82
{
83
    cairo_surface_t *similar;
84
    cairo_bool_t ret;
85

            
86
    similar = cairo_surface_create_similar (surface,
87
	                                    CAIRO_CONTENT_COLOR_ALPHA,
88
					    1, 1);
89
    if (cairo_surface_status (similar))
90
	return FALSE;
91

            
92
    ret = surface_compare_visual_and_format (similar);
93

            
94
    cairo_surface_destroy (similar);
95

            
96
    return ret;
97
}
98

            
99

            
100
static void
101
draw_pattern (cairo_surface_t *surface)
102
{
103
    cairo_t *cr = cairo_create (surface);
104
    int i;
105

            
106
    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
107
    cairo_paint (cr);
108

            
109
    cairo_set_source_rgba (cr, 0, 0.0, 0.0, 0.50); /* half-alpha-black */
110

            
111
    for (i = 1; i <= 3; i++) {
112
	int inset = SIZE / 8 * i;
113

            
114
	cairo_rectangle (cr,
115
			 inset,            inset,
116
			 SIZE - 2 * inset, SIZE - 2 * inset);
117
	cairo_fill (cr);
118
    }
119

            
120
    cairo_destroy (cr);
121
}
122

            
123
static void
124
erase_pattern (cairo_surface_t *surface)
125
{
126
    cairo_t *cr = cairo_create (surface);
127

            
128
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
129
    cairo_paint (cr);
130

            
131
    cairo_destroy (cr);
132
}
133

            
134
static cairo_test_status_t
135
do_test (const cairo_test_context_t *ctx,
136
	 Display        *dpy,
137
	 unsigned char  *reference_data,
138
	 unsigned char  *test_data,
139
	 unsigned char  *diff_data,
140
	 cairo_bool_t    use_pixmap,
141
	 cairo_bool_t    set_size,
142
	 cairo_bool_t    offscreen)
143
{
144
    cairo_surface_t *surface;
145
    cairo_surface_t *test_surface;
146
    cairo_t *test_cr;
147
    buffer_diff_result_t result;
148
    Drawable drawable;
149
    int screen = DefaultScreen (dpy);
150

            
151
    if (use_pixmap && offscreen)
152
	return CAIRO_TEST_SUCCESS;
153

            
154
    if (use_pixmap) {
155
	drawable = XCreatePixmap (dpy, DefaultRootWindow (dpy),
156
				  SIZE, SIZE, DefaultDepth (dpy, screen));
157
    } else {
158
	XSetWindowAttributes xwa;
159
	int x, y;
160

            
161
	xwa.override_redirect = True;
162

            
163
	if (offscreen) {
164
	    x = - OFFSCREEN_OFFSET;
165
	    y = - OFFSCREEN_OFFSET;
166
	} else {
167
	    x = 0;
168
	    y = 0;
169
	}
170

            
171
	drawable = XCreateWindow (dpy, DefaultRootWindow (dpy),
172
				  x, y, SIZE, SIZE, 0,
173
				  DefaultDepth (dpy, screen), InputOutput,
174
				  DefaultVisual (dpy, screen),
175
				  CWOverrideRedirect, &xwa);
176
	XMapWindow (dpy, drawable);
177
    }
178

            
179
    surface = cairo_xlib_surface_create (dpy,
180
					 drawable,
181
					 DefaultVisual (dpy, screen),
182
					 SIZE, SIZE);
183

            
184
    if (! surface_compare_visual_and_format (surface))
185
	return CAIRO_TEST_FAILURE;
186

            
187
    if (set_size) {
188
	cairo_xlib_surface_set_size (surface, SIZE, SIZE);
189

            
190
	if (cairo_xlib_surface_get_width (surface) != SIZE ||
191
	    cairo_xlib_surface_get_height (surface) != SIZE)
192
	    return CAIRO_TEST_FAILURE;
193
    }
194

            
195
    if (! check_similar_visual_and_format (surface))
196
	return CAIRO_TEST_FAILURE;
197

            
198
    draw_pattern (surface);
199

            
200
    test_surface = cairo_image_surface_create_for_data (test_data,
201
							CAIRO_FORMAT_RGB24,
202
							SIZE, SIZE,
203
							SIZE * 4);
204

            
205
    test_cr = cairo_create (test_surface);
206
    cairo_set_source_surface (test_cr, surface, 0, 0);
207
    cairo_paint (test_cr);
208

            
209
    cairo_destroy (test_cr);
210
    cairo_surface_destroy (test_surface);
211

            
212
    /* We erase the surface to black in case we get the same
213
     * memory back again for the pixmap case.
214
     */
215
    erase_pattern (surface);
216
    cairo_surface_destroy (surface);
217

            
218
    if (use_pixmap)
219
	XFreePixmap (dpy, drawable);
220
    else
221
	XDestroyWindow (dpy, drawable);
222

            
223
    if (offscreen) {
224
	size_t offset = 4 * (SIZE * OFFSCREEN_OFFSET + OFFSCREEN_OFFSET);
225

            
226
	buffer_diff_noalpha (reference_data + offset,
227
			     test_data + offset,
228
			     diff_data + offset,
229
			     SIZE - OFFSCREEN_OFFSET,
230
			     SIZE - OFFSCREEN_OFFSET,
231
			     4 * SIZE,
232
			     &result);
233
    } else {
234
	buffer_diff_noalpha (reference_data,
235
			     test_data,
236
			     diff_data,
237
			     SIZE,
238
			     SIZE,
239
			     4 * SIZE,
240
			     &result);
241
    }
242

            
243
    cairo_test_log (ctx, "xlib-surface: %s, %s, %s: %s\n",
244
		    set_size ? "   size" : "no-size",
245
		    use_pixmap ? "pixmap" : "window",
246
		    use_pixmap ?  "           " : (offscreen ? ", offscreen" : ",  onscreen"),
247
		    image_diff_is_failure (&result, 0) ? "FAIL" : "PASS");
248

            
249
    if (image_diff_is_failure (&result, 0))
250
	return CAIRO_TEST_FAILURE;
251
    else
252
	return CAIRO_TEST_SUCCESS;
253
}
254

            
255
static cairo_bool_t
256
check_visual (Display *dpy)
257
{
258
    Visual *visual = DefaultVisual (dpy, DefaultScreen (dpy));
259

            
260
    if ((visual->red_mask   == 0xff0000 &&
261
	 visual->green_mask == 0x00ff00 &&
262
	 visual->blue_mask  == 0x0000ff) ||
263
	(visual->red_mask   == 0x0000ff &&
264
	 visual->green_mask == 0x00ff00 &&
265
	 visual->blue_mask  == 0xff0000))
266
	return 1;
267
    else
268
	return 0;
269
}
270

            
271
#undef xcalloc
272
static void *
273
xcalloc (const cairo_test_context_t *ctx, size_t a, size_t b)
274
{
275
    void *ptr = calloc (a, b);
276
    if (ptr == NULL) {
277
	cairo_test_log (ctx, "xlib-surface: unable to allocate memory, skipping\n");
278
	abort ();
279
    }
280
    return ptr;
281
}
282

            
283
static cairo_test_status_t
284
1
preamble (cairo_test_context_t *ctx)
285
{
286
    Display *dpy;
287
    unsigned char *reference_data;
288
    unsigned char *test_data;
289
    unsigned char *diff_data;
290
    cairo_surface_t *reference_surface;
291
    cairo_bool_t use_pixmap;
292
    cairo_bool_t set_size;
293
    cairo_bool_t offscreen;
294
1
    cairo_test_status_t status, result = CAIRO_TEST_UNTESTED;
295
    int stride;
296

            
297
1
    if (! cairo_test_is_target_enabled (ctx, "xlib"))
298
1
	goto CLEANUP_TEST;
299

            
300
    dpy = XOpenDisplay (NULL);
301
    if (!dpy) {
302
	cairo_test_log (ctx, "xlib-surface: Cannot open display, skipping\n");
303
	goto CLEANUP_TEST;
304
    }
305

            
306
    if (!check_visual (dpy)) {
307
	cairo_test_log (ctx, "xlib-surface: default visual is not RGB24 or BGR24, skipping\n");
308
	goto CLEANUP_DISPLAY;
309
    }
310

            
311
    stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, SIZE);
312

            
313
    reference_data = xcalloc (ctx, SIZE, stride);
314
    test_data = xcalloc (ctx, SIZE, stride);
315
    diff_data = xcalloc (ctx, SIZE, stride);
316

            
317
    reference_surface = cairo_image_surface_create_for_data (reference_data,
318
							     CAIRO_FORMAT_RGB24,
319
							     SIZE, SIZE,
320
							     stride);
321

            
322
    draw_pattern (reference_surface);
323
    cairo_surface_destroy (reference_surface);
324

            
325
    result = CAIRO_TEST_SUCCESS;
326

            
327
    for (set_size = 0; set_size <= 1; set_size++)
328
	for (use_pixmap = 0; use_pixmap <= 1; use_pixmap++)
329
	    for (offscreen = 0; offscreen <= 1; offscreen++) {
330
		status = do_test (ctx, dpy,
331
				  reference_data, test_data, diff_data,
332
				  use_pixmap, set_size, offscreen);
333
		if (status)
334
		    result = status;
335
	    }
336

            
337
    free (reference_data);
338
    free (test_data);
339
    free (diff_data);
340

            
341
  CLEANUP_DISPLAY:
342
    XCloseDisplay (dpy);
343

            
344
1
  CLEANUP_TEST:
345
1
    return result;
346
}
347

            
348
1
CAIRO_TEST (xlib_surface,
349
	    "Check creating surfaces for various XWindows",
350
	    "xlib", /* keywords */
351
	    NULL, /* requirements */
352
	    0, 0,
353
	    preamble, NULL)