1#include <gtk/gtk.h>
2
3#include <math.h>
4#include <stdlib.h>
5
6static void
7hsv_to_rgb (GdkRGBA *rgba,
8 double h,
9 double s,
10 double v)
11{
12 double hue, saturation, value;
13 double f, p, q, t;
14
15 rgba->alpha = 1.0;
16
17 if ( s == 0.0)
18 {
19 rgba->red = v;
20 rgba->green = v;
21 rgba->blue = v; /* heh */
22 }
23 else
24 {
25 hue = h * 6.0;
26 saturation = s;
27 value = v;
28
29 if (hue == 6.0)
30 hue = 0.0;
31
32 f = hue - (int) hue;
33 p = value * (1.0 - saturation);
34 q = value * (1.0 - saturation * f);
35 t = value * (1.0 - saturation * (1.0 - f));
36
37 switch ((int) hue)
38 {
39 case 0:
40 rgba->red = value;
41 rgba->green = t;
42 rgba->blue = p;
43 break;
44
45 case 1:
46 rgba->red = q;
47 rgba->green = value;
48 rgba->blue = p;
49 break;
50
51 case 2:
52 rgba->red = p;
53 rgba->green = value;
54 rgba->blue = t;
55 break;
56
57 case 3:
58 rgba->red = p;
59 rgba->green = q;
60 rgba->blue = value;
61 break;
62
63 case 4:
64 rgba->red = t;
65 rgba->green = p;
66 rgba->blue = value;
67 break;
68
69 case 5:
70 rgba->red = value;
71 rgba->green = p;
72 rgba->blue = q;
73 break;
74
75 default:
76 g_assert_not_reached ();
77 }
78 }
79}
80
81static GskRenderNode *
82rounded_borders (guint n)
83{
84 GskRenderNode **nodes = g_newa (GskRenderNode *, n);
85 GskRenderNode *container;
86 GskRoundedRect outline;
87 float widths[4];
88 GdkRGBA colors[4];
89 guint i;
90
91 for (i = 0; i < n; i++)
92 {
93 outline.bounds.size.width = g_random_int_range (begin: 20, end: 1000);
94 outline.bounds.origin.x = g_random_int_range (begin: 0, end: 1000 - outline.bounds.size.width);
95 outline.bounds.size.height = g_random_int_range (begin: 20, end: 1000);
96 outline.bounds.origin.y = g_random_int_range (begin: 0, end: 1000 - outline.bounds.size.height);
97 outline.corner[0].width = outline.corner[0].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
98 outline.corner[1].width = outline.corner[1].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
99 outline.corner[2].width = outline.corner[2].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
100 outline.corner[3].width = outline.corner[3].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
101 widths[0] = widths[1] = widths[2] = widths[3] = g_random_int_range (begin: 0, end: 5);
102 hsv_to_rgb (rgba: &colors[0], h: g_random_double (), s: 1.0, v: 1.0);
103 colors[3] = colors[2] = colors[1] = colors[0];
104 nodes[i] = gsk_border_node_new (outline: &outline, border_width: widths, border_color: colors);
105 }
106
107 container = gsk_container_node_new (children: nodes, n_children: n);
108
109 for (i = 0; i < n; i++)
110 gsk_render_node_unref (node: nodes[i]);
111
112 return container;
113}
114
115static GskRenderNode *
116rounded_backgrounds (guint n)
117{
118 GskRenderNode **nodes = g_newa (GskRenderNode *, n);
119 GskRenderNode *container, *texture;
120 GskRoundedRect outline;
121 GdkRGBA color;
122 guint i;
123
124 for (i = 0; i < n; i++)
125 {
126 outline.bounds.size.width = g_random_int_range (begin: 20, end: 100);
127 outline.bounds.origin.x = g_random_int_range (begin: 0, end: 1000 - outline.bounds.size.width);
128 outline.bounds.size.height = g_random_int_range (begin: 20, end: 100);
129 outline.bounds.origin.y = g_random_int_range (begin: 0, end: 1000 - outline.bounds.size.height);
130 outline.corner[0].width = outline.corner[0].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
131 outline.corner[1].width = outline.corner[1].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
132 outline.corner[2].width = outline.corner[2].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
133 outline.corner[3].width = outline.corner[3].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
134 hsv_to_rgb (rgba: &color, h: g_random_double (), s: g_random_double_range (begin: 0.15, end: 0.4), v: g_random_double_range (begin: 0.6, end: 0.85));
135 color.alpha = g_random_double_range (begin: 0.5, end: 0.75);
136 texture = gsk_color_node_new (rgba: &color, bounds: &outline.bounds);
137 nodes[i] = gsk_rounded_clip_node_new (child: texture, clip: &outline);
138 }
139
140 container = gsk_container_node_new (children: nodes, n_children: n);
141
142 for (i = 0; i < n; i++)
143 gsk_render_node_unref (node: nodes[i]);
144
145 return container;
146}
147
148static GskRenderNode *
149colors (guint n)
150{
151 GskRenderNode **nodes = g_new (GskRenderNode *, 10 * n);
152 GskRenderNode *container;
153 graphene_rect_t bounds;
154 GdkRGBA color;
155 guint i;
156
157 for (i = 0; i < 10 * n; i++)
158 {
159 bounds.size.width = g_random_int_range (begin: 20, end: 100);
160 bounds.origin.x = g_random_int_range (begin: 0, end: 1000 - bounds.size.width);
161 bounds.size.height = g_random_int_range (begin: 20, end: 100);
162 bounds.origin.y = g_random_int_range (begin: 0, end: 1000 - bounds.size.height);
163 hsv_to_rgb (rgba: &color, h: g_random_double (), s: g_random_double_range (begin: 0.15, end: 0.4), v: g_random_double_range (begin: 0.6, end: 0.85));
164 color.alpha = g_random_double_range (begin: 0.5, end: 0.75);
165 nodes[i] = gsk_color_node_new (rgba: &color, bounds: &bounds);
166 }
167
168 container = gsk_container_node_new (children: nodes, n_children: 10 * n);
169
170 for (i = 0; i < 10 * n; i++)
171 gsk_render_node_unref (node: nodes[i]);
172 g_free (mem: nodes);
173
174 return container;
175}
176
177static GskRenderNode *
178clipped_colors (guint n)
179{
180 GskRenderNode **nodes = g_newa (GskRenderNode *,n);
181 GskRenderNode *container;
182 graphene_rect_t bounds;
183 GdkRGBA color;
184 guint i;
185
186 for (i = 0; i < n; i++)
187 {
188 bounds.size.width = g_random_int_range (begin: 20, end: 100);
189 bounds.origin.x = g_random_int_range (begin: 0, end: 1000 - bounds.size.width);
190 bounds.size.height = g_random_int_range (begin: 20, end: 100);
191 bounds.origin.y = g_random_int_range (begin: 0, end: 1000 - bounds.size.height);
192 hsv_to_rgb (rgba: &color, h: g_random_double (), s: g_random_double_range (begin: 0.15, end: 0.4), v: g_random_double_range (begin: 0.6, end: 0.85));
193 color.alpha = g_random_double_range (begin: 0.5, end: 0.75);
194 nodes[i] = gsk_color_node_new (rgba: &color, bounds: &bounds);
195 }
196
197 container = gsk_container_node_new (children: nodes, n_children: n);
198
199 for (i = 0; i < n; i++)
200 gsk_render_node_unref (node: nodes[i]);
201
202#define GRID_SIZE 4
203 for (i = 0; i < GRID_SIZE * GRID_SIZE; i++)
204 {
205 guint x = i % GRID_SIZE;
206 guint y = i / GRID_SIZE;
207
208 if ((x + y) % 2)
209 continue;
210
211 nodes[i / 2] = gsk_clip_node_new (child: container,
212 clip: &GRAPHENE_RECT_INIT(
213 x * 1000 / GRID_SIZE, y * 1000 / GRID_SIZE,
214 1000 / GRID_SIZE, 1000 / GRID_SIZE
215 ));
216 }
217
218 gsk_render_node_unref (node: container);
219
220 container = gsk_container_node_new (children: nodes, GRID_SIZE * GRID_SIZE / 2);
221
222 for (i = 0; i < GRID_SIZE * GRID_SIZE / 2; i++)
223 gsk_render_node_unref (node: nodes[i]);
224
225 return container;
226}
227
228static int
229compare_color_stops (gconstpointer a,
230 gconstpointer b,
231 gpointer user_data)
232{
233 const GskColorStop *stopa = a;
234 const GskColorStop *stopb = b;
235
236 if (stopa->offset < stopb->offset)
237 return -1;
238 else if (stopa->offset > stopb->offset)
239 return 1;
240 else
241 return 0;
242}
243
244static GskRenderNode *
245linear_gradient (guint n)
246{
247 GskRenderNode **nodes = g_newa (GskRenderNode *, n);
248 GskRenderNode *container;
249 graphene_rect_t bounds;
250 GskColorStop stops[5];
251 graphene_point_t start, end;
252 guint i, j, n_stops;
253
254 for (i = 0; i < n; i++)
255 {
256 bounds.size.width = g_random_int_range (begin: 20, end: 100);
257 bounds.origin.x = g_random_int_range (begin: 0, end: 1000 - bounds.size.width);
258 bounds.size.height = g_random_int_range (begin: 20, end: 100);
259 bounds.origin.y = g_random_int_range (begin: 0, end: 1000 - bounds.size.height);
260 do {
261 start.x = g_random_double_range (begin: - bounds.size.width / 4, end: bounds.size.width / 4);
262 if (start.x >= 0)
263 start.x += bounds.origin.x;
264 else
265 start.x += bounds.origin.x + bounds.size.width;
266 start.y = g_random_double_range (begin: - bounds.size.height / 4, end: bounds.size.height / 4);
267 if (start.y >= 0)
268 start.y += bounds.origin.y;
269 else
270 start.y += bounds.origin.y + bounds.size.height;
271 end.x = g_random_double_range (begin: - bounds.size.width / 4, end: bounds.size.width / 4);
272 if (end.x >= 0)
273 end.x += bounds.origin.x;
274 else
275 end.x += bounds.origin.x + bounds.size.width;
276 end.y = g_random_double_range (begin: - bounds.size.height / 4, end: bounds.size.height / 4);
277 if (end.y >= 0)
278 end.y += bounds.origin.y;
279 else
280 end.y += bounds.origin.y + bounds.size.height;
281 } while (graphene_point_equal (a: &start, b: &end));
282 n_stops = g_random_int_range (begin: 2, end: 5);
283 for (j = 0; j < n_stops; j++)
284 {
285 if (j == 0)
286 stops[j].offset = 0;
287 else if (j == n_stops - 1)
288 stops[j].offset = 1;
289 else
290 stops[j].offset = g_random_double_range (begin: 0, end: 1);
291 hsv_to_rgb (rgba: &stops[j].color, h: g_random_double (), s: g_random_double_range (begin: 0.15, end: 0.4), v: g_random_double_range (begin: 0.6, end: 0.85));
292 stops[j].color.alpha = g_random_double_range (begin: 0, end: 1);
293 }
294 g_qsort_with_data (pbase: stops, total_elems: n_stops, size: sizeof (stops[0]), compare_func: compare_color_stops, user_data: 0);
295 if (g_random_boolean ())
296 nodes[i] = gsk_linear_gradient_node_new (bounds: &bounds, start: &start, end: &end, color_stops: stops, n_color_stops: n_stops);
297 else
298 nodes[i] = gsk_repeating_linear_gradient_node_new (bounds: &bounds, start: &start, end: &end, color_stops: stops, n_color_stops: n_stops);
299 }
300
301 container = gsk_container_node_new (children: nodes, n_children: n);
302
303 for (i = 0; i < n; i++)
304 gsk_render_node_unref (node: nodes[i]);
305
306 return container;
307}
308
309static GskRenderNode *
310borders (guint n)
311{
312 GskRenderNode **nodes = g_newa (GskRenderNode *, n);
313 GskRenderNode *container;
314 GskRoundedRect outline;
315 GdkRGBA colors[4];
316 float widths[4];
317 guint i, j;
318
319 for (i = 0; i < n; i++)
320 {
321 outline.bounds.size.width = g_random_int_range (begin: 20, end: 100);
322 outline.bounds.origin.x = g_random_int_range (begin: 0, end: 1000 - outline.bounds.size.width);
323 outline.bounds.size.height = g_random_int_range (begin: 20, end: 100);
324 outline.bounds.origin.y = g_random_int_range (begin: 0, end: 1000 - outline.bounds.size.height);
325 outline.corner[1].width = outline.corner[1].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
326 outline.corner[2].width = outline.corner[2].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
327 outline.corner[3].width = outline.corner[3].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
328 for (j = 0; j < 4; j++)
329 {
330 outline.corner[0].width = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
331 outline.corner[0].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
332 hsv_to_rgb (rgba: &colors[j], h: g_random_double (), s: 1.0, v: 0.5); //g_random_double_range (0.15, 0.4), g_random_double_range (0.6, 0.85));
333 colors[j].alpha = 1.0; //g_random_double_range (0.8, 1.0);
334 widths[j] = g_random_int_range (begin: 1, end: 6);
335 }
336 nodes[i] = gsk_border_node_new (outline: &outline, border_width: widths, border_color: colors);
337 }
338
339 container = gsk_container_node_new (children: nodes, n_children: n);
340
341 for (i = 0; i < n; i++)
342 gsk_render_node_unref (node: nodes[i]);
343
344 return container;
345}
346
347const char *example =
348"'Twas brillig, and the slithy toves\n"
349"Did gyre and gimble in the wabe;\n"
350"All mimsy were the borogoves,\n"
351"And the mome raths outgrabe.\n"
352"\n"
353"'Beware the Jabberwock, my son!\n"
354"The jaws that bite, the claws that catch!\n"
355"Beware the Jubjub bird, and shun\n"
356"The frumious Bandersnatch!'";
357
358static GskRenderNode *
359text (guint n)
360{
361 GPtrArray *nodes;
362 GskRenderNode *container;
363 PangoFontDescription *desc;
364 PangoContext *context;
365 PangoLayout *layout;
366 GtkSettings *settings;
367 char *usr_dict_words;
368 char **words;
369 gsize n_words;
370 int dpi_int;
371 int i;
372
373 if (g_file_get_contents (filename: "/usr/share/dict/words", contents: &usr_dict_words, NULL, NULL))
374 {
375 words = g_strsplit (string: usr_dict_words, delimiter: "\n", max_tokens: -1);
376 g_free (mem: usr_dict_words);
377 }
378 else
379 {
380 words = g_strsplit (string: "the quick brown fox jumps over the lazy dog", delimiter: " ", max_tokens: -1);
381 }
382 n_words = g_strv_length (str_array: words);
383
384 context = pango_font_map_create_context (fontmap: pango_cairo_font_map_get_default ());
385 nodes = g_ptr_array_new_with_free_func (element_free_func: (GDestroyNotify) gsk_render_node_unref);
386
387 settings = gtk_settings_get_default ();
388 g_object_get (object: settings, first_property_name: "gtk-xft-dpi", &dpi_int, NULL);
389 if (dpi_int > 0)
390 pango_cairo_context_set_resolution (context, dpi: dpi_int / 1024.);
391
392 desc = pango_font_description_new ();
393 pango_font_description_set_family (desc, family: "Cantarell");
394 layout = pango_layout_new (context);
395
396 for (i = 0; i < n; i++)
397 {
398 PangoFont *font;
399 PangoLayoutIter *iter;
400 PangoLayoutRun *run;
401 GdkRGBA color;
402 int x, y, width, height;
403
404 pango_layout_set_text (layout, text: words[g_random_int_range (begin: 0, end: n_words)], length: -1);
405 if (g_random_boolean ())
406 pango_font_description_set_style (desc, style: PANGO_STYLE_ITALIC);
407 else
408 pango_font_description_set_style (desc, style: PANGO_STYLE_NORMAL);
409
410 pango_font_description_set_weight (desc, weight: 100 * g_random_int_range (begin: 1, end: 10));
411 pango_font_description_set_size (desc, PANGO_SCALE * g_random_int_range (begin: 8,end: 32));
412
413 font = pango_context_load_font (context, desc);
414 pango_layout_set_font_description (layout, desc);
415
416 pango_layout_get_pixel_size (layout, width: &width, height: &height);
417 x = width >= 1000 ? 0 : g_random_int_range (begin: 0, end: 1000 - width);
418 y = height >= 1000 ? 0 : g_random_int_range (begin: 0, end: 1000 - height);
419 hsv_to_rgb (rgba: &color, h: g_random_double (), s: g_random_double_range (begin: 0.5, end: 1.0), v: g_random_double_range (begin: 0.15, end: 0.75));
420
421 iter = pango_layout_get_iter (layout);
422 do
423 {
424 run = pango_layout_iter_get_run (iter);
425 if (run != NULL)
426 {
427 GskRenderNode *node;
428
429 node = gsk_text_node_new (font, glyphs: run->glyphs, color: &color, offset: &GRAPHENE_POINT_INIT (x, y));
430 if (node)
431 g_ptr_array_add (array: nodes, data: node);
432 }
433 }
434 while (pango_layout_iter_next_run (iter));
435 pango_layout_iter_free (iter);
436
437 g_object_unref (object: font);
438 }
439
440 pango_font_description_free (desc);
441 g_object_unref (object: layout);
442
443 container = gsk_container_node_new (children: (GskRenderNode **) nodes->pdata, n_children: nodes->len);
444 g_ptr_array_unref (array: nodes);
445 g_strfreev (str_array: words);
446
447 return container;
448}
449
450static GskRenderNode *
451cairo_node (guint n)
452{
453 GskRenderNode **nodes = g_newa (GskRenderNode *, n);
454 GskRenderNode *container;
455 graphene_rect_t bounds;
456 guint i;
457
458 for (i = 0; i < n; i++)
459 {
460 bounds.size.width = g_random_int_range (begin: 20, end: 100);
461 bounds.origin.x = g_random_int_range (begin: 0, end: 1000 - bounds.size.width);
462 bounds.size.height = g_random_int_range (begin: 20, end: 100);
463 bounds.origin.y = g_random_int_range (begin: 0, end: 1000 - bounds.size.height);
464 nodes [i] = gsk_cairo_node_new (bounds: &bounds);
465 }
466
467 container = gsk_container_node_new (children: nodes, n_children: n);
468
469 for (i = 0; i < n; i++)
470 gsk_render_node_unref (node: nodes[i]);
471
472 return container;
473}
474
475static GskRenderNode *
476box_shadows (guint n)
477{
478 GskRenderNode **nodes = g_newa (GskRenderNode *, n);
479 GskRenderNode *container;
480 int i, j;
481 GskRoundedRect outline;
482 GdkRGBA color;
483 float dx, dy;
484 float spread;
485 float blur;
486
487 for (i = 0; i < n; i++)
488 {
489 outline.bounds.size.width = g_random_int_range (begin: 20, end: 100);
490 outline.bounds.origin.x = g_random_int_range (begin: 0, end: 1000 - outline.bounds.size.width);
491 outline.bounds.size.height = g_random_int_range (begin: 20, end: 100);
492 outline.bounds.origin.y = g_random_int_range (begin: 0, end: 1000 - outline.bounds.size.height);
493 outline.corner[1].width = outline.corner[1].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
494 outline.corner[2].width = outline.corner[2].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
495 outline.corner[3].width = outline.corner[3].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
496 for (j = 0; j < 4; j++)
497 {
498 outline.corner[0].width = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
499 outline.corner[0].height = 10 - (int) sqrt (x: g_random_int_range (begin: 0, end: 100));
500 }
501
502 hsv_to_rgb (rgba: &color, h: g_random_double (), s: g_random_double_range (begin: 0.15, end: 0.4), v: g_random_double_range (begin: 0.6, end: 0.85));
503
504 dx = g_random_double_range (begin: 0.0, end: 5.0);
505 dy = g_random_double_range (begin: 0.0, end: 5.0);
506 spread = g_random_double_range (begin: 0.0, end: 10.0);
507 blur = g_random_double_range (begin: 0.0, end: 10.0);
508
509 if (g_random_boolean ())
510 nodes[i] = gsk_inset_shadow_node_new (outline: &outline, color: &color, dx, dy, spread, blur_radius: blur);
511 else
512 nodes[i] = gsk_outset_shadow_node_new (outline: &outline, color: &color, dx, dy, spread, blur_radius: blur);
513 }
514
515 container = gsk_container_node_new (children: nodes, n_children: n);
516
517 for (i = 0; i < n; i++)
518 gsk_render_node_unref (node: nodes[i]);
519
520 return container;
521}
522
523int
524main (int argc, char **argv)
525{
526 static const struct {
527 const char *name;
528 GskRenderNode * (* func) (guint n);
529 } functions[] = {
530 { "cairo.node", cairo_node },
531 { "colors.node", colors },
532 { "clipped-colors.node", clipped_colors },
533 { "rounded-borders.node", rounded_borders },
534 { "rounded-backgrounds.node", rounded_backgrounds },
535 { "linear-gradient.node", linear_gradient },
536 { "borders.node", borders },
537 { "text.node", text },
538 { "box-shadows.node", box_shadows },
539 };
540 GError *error = NULL;
541 GskRenderNode *node;
542 GPatternSpec *matcher;
543 const char *pattern;
544 guint i, n;
545
546 gtk_init ();
547
548 n = 100000;
549 pattern = "*";
550
551 if (argc > 1)
552 {
553 if (argc > 2)
554 pattern = argv[2];
555 n = atoi (nptr: argv[1]);
556 }
557
558 matcher = g_pattern_spec_new (pattern);
559
560 for (i = 0; i < G_N_ELEMENTS (functions); i++)
561 {
562 if (!g_pattern_match_string (pspec: matcher, string: functions[i].name))
563 continue;
564
565 node = functions[i].func (n);
566 if (!gsk_render_node_write_to_file (node, filename: functions[i].name, error: &error))
567 {
568 g_print (format: "Error writing \"%s\": %s\n", functions[i].name, error->message);
569 g_clear_error (err: &error);
570 return 1;
571 }
572 gsk_render_node_unref (node);
573 g_print (format: "Created test file \"%s\".\n", functions[i].name);
574 }
575
576 g_pattern_spec_free (pspec: matcher);
577
578 return 0;
579}
580

source code of gtk/tests/rendernode-create-tests.c