1
/*
2
 * Copyright © 2007 Chris Wilson
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
 * Chris Wilson not be used in advertising or publicity pertaining to
10
 * distribution of the software without specific, written prior
11
 * permission. Chris Wilson 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
 * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17
 * FITNESS, IN NO EVENT SHALL CHRIS WILSON 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: Chris Wilson <chris@chris-wilson.co.uk>
24
 *
25
 * Inspiration (and path!) taken from
26
 * http://labs.trolltech.com/blogs/2007/08/31/rasterizing-dragons/
27
 */
28

            
29
#include "cairo-perf.h"
30

            
31
static inline int
32
next_pot (int v)
33
{
34
    v--;
35
    v |= v >> 1;
36
    v |= v >> 2;
37
    v |= v >> 4;
38
    v |= v >> 8;
39
    v |= v >> 16;
40
    v++;
41
    return v;
42
}
43

            
44
static cairo_bool_t
45
direction (int i)
46
{
47
    int pivot, np2;
48

            
49
    if (i < 2)
50
	return TRUE;
51

            
52
    np2 = next_pot (i + 1);
53
    if (np2 == i + 1)
54
	return TRUE;
55

            
56
    pivot = np2 / 2 - 1;
57
    return ! direction (2 * pivot - i);
58
}
59

            
60
static void
61
path (cairo_t *cr, int step, int dir, int iterations)
62
{
63
    double dx, dy;
64
    int i;
65

            
66
    switch (dir) {
67
	default:
68
	case 0: dx =  step; dy =  0; break;
69
	case 1: dx = -step; dy =  0; break;
70
	case 2: dx =  0; dy =  step; break;
71
	case 3: dx =  0; dy = -step; break;
72
    }
73

            
74
    for (i = 0; i < iterations; i++) {
75
	cairo_rel_line_to (cr, dx, dy);
76

            
77
	if (direction (i)) {
78
	    double t = dx;
79
	    dx = dy;
80
	    dy = -t;
81
	} else {
82
	    double t = dx;
83
	    dx = -dy;
84
	    dy = t;
85
	}
86
    }
87
}
88

            
89
static cairo_time_t
90
do_dragon (cairo_t *cr, int width, int height, int loops)
91
{
92
    cairo_pattern_t *pattern;
93
    double cx, cy, r;
94

            
95
    cx = cy = .5 * MAX (width, height);
96
    r = .5 * MIN (width, height);
97

            
98
    cairo_perf_timer_start ();
99

            
100
    while (loops--) {
101
	pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
102
	cairo_pattern_add_color_stop_rgb (pattern, 0., .0, .0, .0);
103
	cairo_pattern_add_color_stop_rgb (pattern, 0.25, .5, .4, .4);
104
	cairo_pattern_add_color_stop_rgb (pattern, .5, .8, .8, .9);
105
	cairo_pattern_add_color_stop_rgb (pattern, 1., .9, .9, 1.);
106
	cairo_set_source (cr, pattern);
107
	cairo_pattern_destroy (pattern);
108
	cairo_paint (cr);
109

            
110
	cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
111
	cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
112
	cairo_set_line_width (cr, 4.);
113

            
114
	cairo_move_to (cr, cx, cy);
115
	path (cr, 12, 0, 2048);
116
	pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
117
	cairo_pattern_add_color_stop_rgb (pattern, 0., 1., 1., 1.);
118
	cairo_pattern_add_color_stop_rgb (pattern, 1., 0., 0., 0.);
119
	cairo_set_source (cr, pattern);
120
	cairo_pattern_destroy (pattern);
121
	cairo_stroke(cr);
122

            
123
	cairo_move_to (cr, cx, cy);
124
	path (cr, 12, 1, 2048);
125
	pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
126
	cairo_pattern_add_color_stop_rgb (pattern, 1., 1., 1., 0.);
127
	cairo_pattern_add_color_stop_rgb (pattern, 0., 1., 0., 0.);
128
	cairo_set_source (cr, pattern);
129
	cairo_pattern_destroy (pattern);
130
	cairo_stroke(cr);
131

            
132
	cairo_move_to (cr, cx, cy);
133
	path (cr, 12, 2, 2048);
134
	pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
135
	cairo_pattern_add_color_stop_rgb (pattern, 1., 0., 1., 1.);
136
	cairo_pattern_add_color_stop_rgb (pattern, 0., 0., 1., 0.);
137
	cairo_set_source (cr, pattern);
138
	cairo_pattern_destroy (pattern);
139
	cairo_stroke(cr);
140

            
141
	cairo_move_to (cr, cx, cy);
142
	path (cr, 12, 3, 2048);
143
	pattern = cairo_pattern_create_radial (cx, cy, 0., cx, cy, r);
144
	cairo_pattern_add_color_stop_rgb (pattern, 1., 1., 0., 1.);
145
	cairo_pattern_add_color_stop_rgb (pattern, 0., 0., 0., 1.);
146
	cairo_set_source (cr, pattern);
147
	cairo_pattern_destroy (pattern);
148
	cairo_stroke(cr);
149
    }
150

            
151
    cairo_perf_timer_stop ();
152

            
153
    return cairo_perf_timer_elapsed ();
154
}
155

            
156
static cairo_time_t
157
do_dragon_solid (cairo_t *cr, int width, int height, int loops)
158
{
159
    double cx, cy;
160

            
161
    cx = cy = .5 * MAX (width, height);
162

            
163
    cairo_perf_timer_start ();
164

            
165
    while (loops--) {
166
	cairo_set_source_rgb (cr, 0, 0, 0);
167
	cairo_paint (cr);
168

            
169
	cairo_set_line_width (cr, 4.);
170

            
171
	cairo_move_to (cr, cx, cy);
172
	path (cr, 12, 0, 2048);
173
	cairo_set_source_rgb (cr, 1, 0, 0);
174
	cairo_stroke(cr);
175

            
176
	cairo_move_to (cr, cx, cy);
177
	path (cr, 12, 1, 2048);
178
	cairo_set_source_rgb (cr, 0, 1, 0);
179
	cairo_stroke(cr);
180

            
181
	cairo_move_to (cr, cx, cy);
182
	path (cr, 12, 2, 2048);
183
	cairo_set_source_rgb (cr, 0, 0, 1);
184
	cairo_stroke(cr);
185

            
186
	cairo_move_to (cr, cx, cy);
187
	path (cr, 12, 3, 2048);
188
	cairo_set_source_rgb (cr, 1, 1, 1);
189
	cairo_stroke(cr);
190
    }
191

            
192
    cairo_perf_timer_stop ();
193

            
194
    return cairo_perf_timer_elapsed ();
195
}
196

            
197
static cairo_time_t
198
do_dragon_solid_unaligned (cairo_t *cr, int width, int height, int loops)
199
{
200
    cairo_translate (cr, 0.01, 0.01);
201
    return do_dragon_solid (cr, width, height, loops);
202
}
203

            
204
static cairo_time_t
205
do_dragon_solid_aligned_clip (cairo_t *cr, int width, int height, int loops)
206
{
207
    cairo_reset_clip (cr);
208
    cairo_rectangle (cr, 10, 10, width/2 + 10, height/2 + 10);
209
    cairo_rectangle (cr, width/2-20, height/2-20, width/2 + 10, height/2 + 10);
210
    cairo_clip (cr);
211

            
212
    return do_dragon_solid (cr, width, height, loops);
213
}
214

            
215
static cairo_time_t
216
do_dragon_unaligned_solid_aligned_clip (cairo_t *cr, int width, int height, int loops)
217
{
218
    cairo_translate (cr, 0.01, 0.01);
219
    cairo_reset_clip (cr);
220
    cairo_rectangle (cr, 10, 10, width/2 + 10, height/2 + 10);
221
    cairo_rectangle (cr, width/2-20, height/2-20, width/2 + 10, height/2 + 10);
222
    cairo_clip (cr);
223

            
224
    return do_dragon_solid (cr, width, height, loops);
225
}
226

            
227
static cairo_time_t
228
do_dragon_solid_unaligned_clip (cairo_t *cr, int width, int height, int loops)
229
{
230
    cairo_reset_clip (cr);
231
    cairo_rectangle (cr, 10.5, 10.5, width/2 + 10, height/2 + 10);
232
    cairo_rectangle (cr, width/2-20, height/2-20, width/2 + 9.5, height/2 + 9.5);
233
    cairo_clip (cr);
234

            
235
    return do_dragon_solid (cr, width, height, loops);
236
}
237

            
238
static cairo_time_t
239
do_dragon_unaligned_solid_unaligned_clip (cairo_t *cr, int width, int height, int loops)
240
{
241
    cairo_translate (cr, 0.01, 0.01);
242
    cairo_reset_clip (cr);
243
    cairo_rectangle (cr, 10.5, 10.5, width/2 + 10, height/2 + 10);
244
    cairo_rectangle (cr, width/2-20, height/2-20, width/2 + 9.5, height/2 + 9.5);
245
    cairo_clip (cr);
246

            
247
    return do_dragon_solid (cr, width, height, loops);
248
}
249

            
250
static cairo_time_t
251
do_dragon_solid_circle_clip (cairo_t *cr, int width, int height, int loops)
252
{
253
    cairo_reset_clip (cr);
254
    cairo_arc (cr, width/2., height/2., MIN (width, height)/2. - 10, 0, 2 * M_PI);
255
    cairo_clip (cr);
256

            
257
    return do_dragon_solid (cr, width, height, loops);
258
}
259

            
260
cairo_bool_t
261
dragon_enabled (cairo_perf_t *perf)
262
{
263
    return cairo_perf_can_run (perf, "dragon", NULL);
264
}
265

            
266
void
267
dragon (cairo_perf_t *perf, cairo_t *cr, int width, int height)
268
{
269
    cairo_perf_run (perf, "dragon-solid", do_dragon_solid, NULL);
270
    cairo_perf_run (perf, "dragon-unaligned-solid", do_dragon_solid_unaligned, NULL);
271
    cairo_perf_run (perf, "dragon-solid-aligned-clip", do_dragon_solid_aligned_clip, NULL);
272
    cairo_perf_run (perf, "dragon-unaligned-solid-aligned-clip", do_dragon_unaligned_solid_aligned_clip, NULL);
273
    cairo_perf_run (perf, "dragon-solid-unaligned-clip", do_dragon_solid_unaligned_clip, NULL);
274
    cairo_perf_run (perf, "dragon-unaligned-solid-unaligned-clip", do_dragon_unaligned_solid_unaligned_clip, NULL);
275
    cairo_perf_run (perf, "dragon-solid-circle-clip", do_dragon_solid_circle_clip, NULL);
276
    cairo_perf_run (perf, "dragon", do_dragon, NULL);
277
}