1
/*
2
 * Copyright 2010 Intel Corporation
3
 *
4
 * Permission is hereby granted, free of charge, to any person
5
 * obtaining a copy of this software and associated documentation
6
 * files (the "Software"), to deal in the Software without
7
 * restriction, including without limitation the rights to use, copy,
8
 * modify, merge, publish, distribute, sublicense, and/or sell copies
9
 * of the Software, and to permit persons to whom the Software is
10
 * furnished to do so, subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 *
24
 * Author: Chris Wilson <chris@chris-wilson.co.uk>
25
 */
26

            
27
#include "cairo-test.h"
28

            
29
/* Test the fidelity of the rasterisation, because Cairo is my favourite
30
 * driver test suite.
31
 */
32

            
33
#define GENERATE_REFERENCE 0
34

            
35
#define WIDTH 256
36
#define HEIGHT 40
37

            
38
#include "../src/cairo-fixed-type-private.h"
39
#define PRECISION (1 << CAIRO_FIXED_FRAC_BITS)
40

            
41
/* XXX beware multithreading! */
42
static uint32_t state;
43

            
44
static uint32_t
45
26689536
hars_petruska_f54_1_random (void)
46
{
47
#define rol(x,k) ((x << k) | (x >> (32-k)))
48
26689536
    return state = (state ^ rol (state, 5) ^ rol (state, 24)) + 0x37798849;
49
#undef rol
50
}
51

            
52
static double
53
26689536
random_offset (int range, int precise, int width)
54
{
55
26689536
    double x = hars_petruska_f54_1_random() / (double) UINT32_MAX * range / width;
56
26689536
    if (precise)
57
737280
	x = floor (x * PRECISION) / PRECISION;
58
26689536
    return x;
59
}
60

            
61
static cairo_test_status_t
62
3
rectangles (cairo_t *cr, int width, int height)
63
{
64
    int x, y, channel;
65

            
66
3
    state = 0x12345678;
67

            
68
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
69
3
    cairo_paint (cr);
70

            
71
#if GENERATE_REFERENCE
72
    for (x = 0; x < width; x++) {
73
	cairo_set_source_rgba (cr, 1, 1, 1, x * x * 1.0 / (width * width));
74
	cairo_rectangle (cr, x, 0, 1, height);
75
	cairo_fill (cr);
76
    }
77
#else
78
3
    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
79
12
    for (channel = 0; channel < 3; channel++) {
80
9
	switch (channel) {
81
3
	default:
82
3
	case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
83
3
	case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
84
3
	case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
85
	}
86

            
87
2313
	for (x = 0; x < width; x++) {
88
94464
	    for (y = 0; y < height; y++) {
89
92160
		double dx = random_offset (width - x, TRUE, width);
90
92160
		double dy = random_offset (width - x, TRUE, width);
91
92160
		cairo_rectangle (cr, x + dx, y + dy, x / (double) width, x / (double) width);
92
	    }
93
	}
94
9
	cairo_fill (cr);
95
    }
96
#endif
97

            
98
3
    return CAIRO_TEST_SUCCESS;
99
}
100

            
101
static cairo_test_status_t
102
3
rhombus (cairo_t *cr, int width, int height)
103
{
104
    int x, y;
105
3
    int internal_size = width / 2;
106

            
107
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
108
3
    cairo_paint (cr);
109

            
110
#if GENERATE_REFERENCE
111
    for (y = 0; y < internal_size; y++) {
112
	for (x = 0; x < internal_size; x++) {
113
	    cairo_set_source_rgba (cr, 1, 1, 1,
114
				   x * y / (2. * internal_size * internal_size));
115
	    cairo_rectangle (cr, 2*x, 2*y, 2, 2);
116
	    cairo_fill (cr);
117
	}
118
    }
119
#else
120
3
    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
121
3
    cairo_set_source_rgb (cr, 1, 1, 1);
122

            
123
771
    for (y = 0; y < internal_size; y++) {
124
768
	double yf = y / (double) internal_size;
125
197376
	for (x = 0; x < internal_size; x++) {
126
196608
	    double xf = x / (double) internal_size;
127

            
128
196608
	    cairo_move_to (cr,
129
196608
			   2*x + 1 - xf,
130
196608
			   2*y + 1);
131
196608
	    cairo_line_to (cr,
132
196608
			   2*x + 1,
133
196608
			   2*y + 1 - yf);
134
196608
	    cairo_line_to (cr,
135
196608
			   2*x + 1 + xf,
136
196608
			   2*y + 1);
137
196608
	    cairo_line_to (cr,
138
196608
			   2*x + 1,
139
196608
			   2*y + 1 + yf);
140
196608
	    cairo_close_path (cr);
141
	}
142
    }
143

            
144
3
    cairo_fill (cr);
145
#endif
146

            
147
3
    return CAIRO_TEST_SUCCESS;
148
}
149

            
150
static cairo_test_status_t
151
3
intersecting_quads (cairo_t *cr, int width, int height)
152
{
153
    int x, y, channel;
154

            
155
3
    state = 0x12345678;
156

            
157
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
158
3
    cairo_paint (cr);
159

            
160
#if GENERATE_REFERENCE
161
    for (x = 0; x < width; x++) {
162
	cairo_set_source_rgba (cr, 1, 1, 1, x * x * 0.5 / (width * width));
163
	cairo_rectangle (cr, x, 0, 1, height);
164
	cairo_fill (cr);
165
    }
166
#else
167
3
    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
168
12
    for (channel = 0; channel < 3; channel++) {
169
9
	switch (channel) {
170
3
	default:
171
3
	case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
172
3
	case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
173
3
	case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
174
	}
175

            
176
2313
	for (x = 0; x < width; x++) {
177
2304
	    double step = x / (double) width;
178
94464
	    for (y = 0; y < height; y++) {
179
92160
		double dx = random_offset (width - x, TRUE, width);
180
92160
		double dy = random_offset (width - x, TRUE, width);
181
92160
		cairo_move_to (cr, x + dx, y + dy);
182
92160
		cairo_rel_line_to (cr, step, step);
183
92160
		cairo_rel_line_to (cr, 0, -step);
184
92160
		cairo_rel_line_to (cr, -step, step);
185
92160
		cairo_close_path (cr);
186
	    }
187
	}
188
9
	cairo_fill (cr);
189
    }
190
#endif
191

            
192
3
    return CAIRO_TEST_SUCCESS;
193
}
194

            
195
static cairo_test_status_t
196
3
intersecting_triangles (cairo_t *cr, int width, int height)
197
{
198
    int x, y, channel;
199

            
200
3
    state = 0x12345678;
201

            
202
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
203
3
    cairo_paint (cr);
204

            
205
#if GENERATE_REFERENCE
206
    for (x = 0; x < width; x++) {
207
	cairo_set_source_rgba (cr, 1, 1, 1, x * x * 0.75 / (width * width));
208
	cairo_rectangle (cr, x, 0, 1, height);
209
	cairo_fill (cr);
210
    }
211
#else
212
3
    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
213
12
    for (channel = 0; channel < 3; channel++) {
214
9
	switch (channel) {
215
3
	default:
216
3
	case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
217
3
	case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
218
3
	case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
219
	}
220

            
221
2313
	for (x = 0; x < width; x++) {
222
2304
	    double step = x / (double) width;
223
94464
	    for (y = 0; y < height; y++) {
224
92160
		double dx = random_offset (width - x, TRUE, width);
225
92160
		double dy = random_offset (width - x, TRUE, width);
226

            
227
		/* left */
228
92160
		cairo_move_to (cr, x + dx, y + dy);
229
92160
		cairo_rel_line_to (cr, 0, step);
230
92160
		cairo_rel_line_to (cr, step, 0);
231
92160
		cairo_close_path (cr);
232

            
233
		/* right, mirrored */
234
92160
		cairo_move_to (cr, x + dx + step, y + dy + step);
235
92160
		cairo_rel_line_to (cr, 0, -step);
236
92160
		cairo_rel_line_to (cr, -step, step);
237
92160
		cairo_close_path (cr);
238
	    }
239
	}
240
9
	cairo_fill (cr);
241
    }
242
#endif
243

            
244
3
    return CAIRO_TEST_SUCCESS;
245
}
246

            
247
static cairo_test_status_t
248
3
triangles (cairo_t *cr, int width, int height)
249
{
250
    int x, y, channel;
251

            
252
3
    state = 0x12345678;
253

            
254
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
255
3
    cairo_paint (cr);
256

            
257
#if GENERATE_REFERENCE
258
    for (x = 0; x < width; x++) {
259
	cairo_set_source_rgba (cr, 1, 1, 1, x * x * 0.5 / (width * width));
260
	cairo_rectangle (cr, x, 0, 1, height);
261
	cairo_fill (cr);
262
    }
263
#else
264
3
    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
265
12
    for (channel = 0; channel < 3; channel++) {
266
9
	switch (channel) {
267
3
	default:
268
3
	case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
269
3
	case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
270
3
	case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
271
	}
272

            
273
2313
	for (x = 0; x < width; x++) {
274
94464
	    for (y = 0; y < height; y++) {
275
92160
		double dx = random_offset (width - x, TRUE, width);
276
92160
		double dy = random_offset (width - x, TRUE, width);
277
92160
		cairo_move_to (cr, x + dx, y + dy);
278
92160
		cairo_rel_line_to (cr, x / (double) width, 0);
279
92160
		cairo_rel_line_to (cr, 0, x / (double) width);
280
92160
		cairo_close_path (cr);
281
	    }
282
	}
283
9
	cairo_fill (cr);
284
    }
285
#endif
286

            
287
3
    return CAIRO_TEST_SUCCESS;
288
}
289

            
290
static cairo_test_status_t
291
3
abutting (cairo_t *cr, int width, int height)
292
{
293
    int x, y;
294

            
295
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
296
3
    cairo_paint (cr);
297

            
298
3
    cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.75);
299

            
300
#if GENERATE_REFERENCE
301
    cairo_paint (cr);
302
#else
303
3
    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
304

            
305
51
    for (y = 0; y < 16; y++) {
306
816
	for (x = 0; x < 16; x++) {
307
768
	    double theta = (y * 16 + x) * M_PI / 512;
308
768
	    double cx = 16 * cos (theta) + x * 16;
309
768
	    double cy = 16 * sin (theta) + y * 16;
310

            
311
768
	    cairo_move_to (cr, x * 16, y * 16);
312
768
	    cairo_line_to (cr, cx, cy);
313
768
	    cairo_line_to (cr, (x + 1) * 16, y * 16);
314
768
	    cairo_fill (cr);
315

            
316
768
	    cairo_move_to (cr, (x + 1) * 16, y * 16);
317
768
	    cairo_line_to (cr, cx, cy);
318
768
	    cairo_line_to (cr, (x + 1) * 16, (y + 1) * 16);
319
768
	    cairo_fill (cr);
320

            
321
768
	    cairo_move_to (cr, (x + 1) * 16, (y + 1) * 16);
322
768
	    cairo_line_to (cr, cx, cy);
323
768
	    cairo_line_to (cr, x * 16, (y + 1) * 16);
324
768
	    cairo_fill (cr);
325

            
326
768
	    cairo_move_to (cr, x * 16, (y + 1) * 16);
327
768
	    cairo_line_to (cr, cx, cy);
328
768
	    cairo_line_to (cr, x * 16, y * 16);
329
768
	    cairo_fill (cr);
330
	}
331
    }
332
#endif
333

            
334
3
    return CAIRO_TEST_SUCCESS;
335
}
336

            
337
static cairo_test_status_t
338
3
column_triangles (cairo_t *cr, int width, int height)
339
{
340
    int x, y, i, channel;
341

            
342
3
    state = 0x12345678;
343

            
344
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
345
3
    cairo_paint (cr);
346

            
347
#if GENERATE_REFERENCE
348
    for (x = 0; x < width; x++) {
349
	cairo_set_source_rgba (cr, 1, 1, 1, x * 0.5 / width);
350
	cairo_rectangle (cr, x, 0, 1, height);
351
	cairo_fill (cr);
352
    }
353
#else
354
3
    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
355
12
    for (channel = 0; channel < 3; channel++) {
356
9
	switch (channel) {
357
3
	default:
358
3
	case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
359
3
	case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
360
3
	case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
361
	}
362

            
363
2313
	for (x = 0; x < width; x++) {
364
2304
	    double step = x / (double) (2 * width);
365
11520
	    for (y = 0; y < height; y++) {
366
2368512
		for (i = 0; i < PRECISION; i++) {
367
2359296
		    double dy = random_offset (width - x, FALSE, width);
368

            
369
		    /*
370
		     * We want to test some sharing of edges to further
371
		     * stress the rasterisers, so instead of using one
372
		     * tall triangle, it is split into two, with vertical
373
		     * edges on either side that may co-align with their
374
		     * neighbours:
375
		     *
376
		     *  s ---  .      ---
377
		     *  t  |   |\      |
378
		     *  e  |   | \     |
379
		     *  p ---  ....    |  2 * step = x / width
380
		     *          \ |    |
381
		     *           \|    |
382
		     *            .   ---
383
		     *        |---|
384
		     *     1 / PRECISION
385
		     *
386
		     * Each column contains two triangles of width one quantum and
387
		     * total height of (x / width), thus the total area covered by all
388
		     * columns in each pixel is .5 * (x / width).
389
		     */
390

            
391
2359296
		    cairo_move_to (cr, x + i / (double) PRECISION, y + dy);
392
2359296
		    cairo_rel_line_to (cr, 0, step);
393
2359296
		    cairo_rel_line_to (cr, 1 / (double) PRECISION, step);
394
2359296
		    cairo_rel_line_to (cr, 0, -step);
395
2359296
		    cairo_close_path (cr);
396
		}
397
9216
		cairo_fill (cr); /* do these per-pixel due to the extra volume of edges */
398
	    }
399
	}
400
    }
401
#endif
402

            
403
3
    return CAIRO_TEST_SUCCESS;
404
}
405

            
406
static cairo_test_status_t
407
3
row_triangles (cairo_t *cr, int width, int height)
408
{
409
    int x, y, i, channel;
410

            
411
3
    state = 0x12345678;
412

            
413
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
414
3
    cairo_paint (cr);
415

            
416
#if GENERATE_REFERENCE
417
    for (x = 0; x < width; x++) {
418
	cairo_set_source_rgba (cr, 1, 1, 1, x * 0.5 / width);
419
	cairo_rectangle (cr, x, 0, 1, height);
420
	cairo_fill (cr);
421
    }
422
#else
423
3
    cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
424
12
    for (channel = 0; channel < 3; channel++) {
425
9
	switch (channel) {
426
3
	default:
427
3
	case 0: cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); break;
428
3
	case 1: cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); break;
429
3
	case 2: cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); break;
430
	}
431

            
432
2313
	for (x = 0; x < width; x++) {
433
2304
	    double step = x / (double) (2 * width);
434
94464
	    for (y = 0; y < height; y++) {
435
23685120
		for (i = 0; i < PRECISION; i++) {
436
23592960
		    double dx = random_offset (width - x, FALSE, width);
437

            
438
		    /* See column_triangles() for a transposed description
439
		     * of this geometry.
440
		     */
441

            
442
23592960
		    cairo_move_to (cr, x + dx, y + i / (double) PRECISION);
443
23592960
		    cairo_rel_line_to (cr,  step, 0);
444
23592960
		    cairo_rel_line_to (cr,  step, 1 / (double) PRECISION);
445
23592960
		    cairo_rel_line_to (cr, -step, 0);
446
23592960
		    cairo_close_path (cr);
447
		}
448
92160
		cairo_fill (cr); /* do these per-pixel due to the extra volume of edges */
449
	    }
450
	}
451
    }
452
#endif
453

            
454
3
    return CAIRO_TEST_SUCCESS;
455
}
456

            
457
1
CAIRO_TEST (coverage_rectangles,
458
	    "Check the fidelity of the rasterisation.",
459
	    NULL, /* keywords */
460
	    "target=raster", /* requirements */
461
	    WIDTH, HEIGHT,
462
	    NULL, rectangles)
463

            
464
1
CAIRO_TEST (coverage_rhombus,
465
	    "Check the fidelity of the rasterisation.",
466
	    NULL, /* keywords */
467
	    "target=raster", /* requirements */
468
	    2*WIDTH, 2*WIDTH,
469
	    NULL, rhombus)
470

            
471
1
CAIRO_TEST (coverage_intersecting_quads,
472
	    "Check the fidelity of the rasterisation.",
473
	    NULL, /* keywords */
474
	    "target=raster", /* requirements */
475
	    WIDTH, HEIGHT,
476
	    NULL, intersecting_quads)
477

            
478
1
CAIRO_TEST (coverage_intersecting_triangles,
479
	    "Check the fidelity of the rasterisation.",
480
	    NULL, /* keywords */
481
	    "target=raster", /* requirements */
482
	    WIDTH, HEIGHT,
483
	    NULL, intersecting_triangles)
484
1
CAIRO_TEST (coverage_row_triangles,
485
	    "Check the fidelity of the rasterisation.",
486
	    NULL, /* keywords */
487
	    "target=raster", /* requirements */
488
	    WIDTH, HEIGHT,
489
	    NULL, row_triangles)
490
1
CAIRO_TEST (coverage_column_triangles,
491
	    "Check the fidelity of the rasterisation.",
492
	    NULL, /* keywords */
493
	    "target=raster", /* requirements */
494
	    /* Smaller height since this test does not vary by y-coordinate */
495
	    WIDTH, 4,
496
	    NULL, column_triangles)
497
1
CAIRO_TEST (coverage_triangles,
498
	    "Check the fidelity of the rasterisation.",
499
	    NULL, /* keywords */
500
	    "target=raster", /* requirements */
501
	    WIDTH, HEIGHT,
502
	    NULL, triangles)
503
1
CAIRO_TEST (coverage_abutting,
504
	    "Check the fidelity of the rasterisation.",
505
	    NULL, /* keywords */
506
	    "target=raster", /* requirements */
507
	    16*16, 16*16,
508
	    NULL, abutting)