1 | /* Example code to show how to use pangocairo to render text |
2 | * projected on a path. |
3 | * |
4 | * |
5 | * Written by Behdad Esfahbod, 2006..2007 |
6 | * |
7 | * Permission to use, copy, modify, distribute, and sell this example |
8 | * for any purpose is hereby granted without fee. |
9 | * It is provided "as is" without express or implied warranty. |
10 | */ |
11 | |
12 | #include <math.h> |
13 | #include <stdlib.h> |
14 | #include <pango/pangocairo.h> |
15 | |
16 | void fancy_cairo_stroke (cairo_t *cr); |
17 | void fancy_cairo_stroke_preserve (cairo_t *cr); |
18 | |
19 | /* A fancy cairo_stroke[_preserve]() that draws points and control |
20 | * points, and connects them together. |
21 | */ |
22 | static void |
23 | _fancy_cairo_stroke (cairo_t *cr, cairo_bool_t preserve) |
24 | { |
25 | int i; |
26 | double line_width; |
27 | cairo_path_t *path; |
28 | cairo_path_data_t *data; |
29 | const double dash[] = {10, 10}; |
30 | |
31 | cairo_save (cr); |
32 | cairo_set_source_rgb (cr, red: 1.0, green: 0.0, blue: 0.0); |
33 | |
34 | line_width = cairo_get_line_width (cr); |
35 | path = cairo_copy_path (cr); |
36 | cairo_new_path (cr); |
37 | |
38 | cairo_save (cr); |
39 | cairo_set_line_width (cr, width: line_width / 3); |
40 | cairo_set_dash (cr, dashes: dash, G_N_ELEMENTS (dash), offset: 0); |
41 | for (i=0; i < path->num_data; i += path->data[i].header.length) { |
42 | data = &path->data[i]; |
43 | switch (data->header.type) { |
44 | case CAIRO_PATH_MOVE_TO: |
45 | case CAIRO_PATH_LINE_TO: |
46 | cairo_move_to (cr, x: data[1].point.x, y: data[1].point.y); |
47 | break; |
48 | case CAIRO_PATH_CURVE_TO: |
49 | cairo_line_to (cr, x: data[1].point.x, y: data[1].point.y); |
50 | cairo_move_to (cr, x: data[2].point.x, y: data[2].point.y); |
51 | cairo_line_to (cr, x: data[3].point.x, y: data[3].point.y); |
52 | break; |
53 | case CAIRO_PATH_CLOSE_PATH: |
54 | break; |
55 | default: |
56 | g_assert_not_reached (); |
57 | } |
58 | } |
59 | cairo_stroke (cr); |
60 | cairo_restore (cr); |
61 | |
62 | cairo_save (cr); |
63 | cairo_set_line_width (cr, width: line_width * 4); |
64 | cairo_set_line_cap (cr, line_cap: CAIRO_LINE_CAP_ROUND); |
65 | for (i=0; i < path->num_data; i += path->data[i].header.length) { |
66 | data = &path->data[i]; |
67 | switch (data->header.type) { |
68 | case CAIRO_PATH_MOVE_TO: |
69 | cairo_move_to (cr, x: data[1].point.x, y: data[1].point.y); |
70 | break; |
71 | case CAIRO_PATH_LINE_TO: |
72 | cairo_rel_line_to (cr, dx: 0, dy: 0); |
73 | cairo_move_to (cr, x: data[1].point.x, y: data[1].point.y); |
74 | break; |
75 | case CAIRO_PATH_CURVE_TO: |
76 | cairo_rel_line_to (cr, dx: 0, dy: 0); |
77 | cairo_move_to (cr, x: data[1].point.x, y: data[1].point.y); |
78 | cairo_rel_line_to (cr, dx: 0, dy: 0); |
79 | cairo_move_to (cr, x: data[2].point.x, y: data[2].point.y); |
80 | cairo_rel_line_to (cr, dx: 0, dy: 0); |
81 | cairo_move_to (cr, x: data[3].point.x, y: data[3].point.y); |
82 | break; |
83 | case CAIRO_PATH_CLOSE_PATH: |
84 | cairo_rel_line_to (cr, dx: 0, dy: 0); |
85 | break; |
86 | default: |
87 | g_assert_not_reached (); |
88 | } |
89 | } |
90 | cairo_rel_line_to (cr, dx: 0, dy: 0); |
91 | cairo_stroke (cr); |
92 | cairo_restore (cr); |
93 | |
94 | for (i=0; i < path->num_data; i += path->data[i].header.length) { |
95 | data = &path->data[i]; |
96 | switch (data->header.type) { |
97 | case CAIRO_PATH_MOVE_TO: |
98 | cairo_move_to (cr, x: data[1].point.x, y: data[1].point.y); |
99 | break; |
100 | case CAIRO_PATH_LINE_TO: |
101 | cairo_line_to (cr, x: data[1].point.x, y: data[1].point.y); |
102 | break; |
103 | case CAIRO_PATH_CURVE_TO: |
104 | cairo_curve_to (cr, x1: data[1].point.x, y1: data[1].point.y, |
105 | x2: data[2].point.x, y2: data[2].point.y, |
106 | x3: data[3].point.x, y3: data[3].point.y); |
107 | break; |
108 | case CAIRO_PATH_CLOSE_PATH: |
109 | cairo_close_path (cr); |
110 | break; |
111 | default: |
112 | g_assert_not_reached (); |
113 | } |
114 | } |
115 | cairo_stroke (cr); |
116 | |
117 | if (preserve) |
118 | cairo_append_path (cr, path); |
119 | |
120 | cairo_path_destroy (path); |
121 | |
122 | cairo_restore (cr); |
123 | } |
124 | |
125 | /* A fancy cairo_stroke() that draws points and control points, and |
126 | * connects them together. |
127 | */ |
128 | void |
129 | fancy_cairo_stroke (cairo_t *cr) |
130 | { |
131 | _fancy_cairo_stroke (cr, FALSE); |
132 | } |
133 | |
134 | /* A fancy cairo_stroke_preserve() that draws points and control |
135 | * points, and connects them together. |
136 | */ |
137 | void |
138 | fancy_cairo_stroke_preserve (cairo_t *cr) |
139 | { |
140 | _fancy_cairo_stroke (cr, TRUE); |
141 | } |
142 | |
143 | |
144 | /* Returns Euclidean distance between two points */ |
145 | static double |
146 | two_points_distance (cairo_path_data_t *a, cairo_path_data_t *b) |
147 | { |
148 | double dx, dy; |
149 | |
150 | dx = b->point.x - a->point.x; |
151 | dy = b->point.y - a->point.y; |
152 | |
153 | return sqrt (x: dx * dx + dy * dy); |
154 | } |
155 | |
156 | /* Returns length of a Bezier curve. |
157 | * Seems like computing that analytically is not easy. The |
158 | * code just flattens the curve using cairo and adds the length |
159 | * of segments. |
160 | */ |
161 | static double |
162 | curve_length (double x0, double y0, |
163 | double x1, double y1, |
164 | double x2, double y2, |
165 | double x3, double y3) |
166 | { |
167 | cairo_surface_t *surface; |
168 | cairo_t *cr; |
169 | cairo_path_t *path; |
170 | cairo_path_data_t *data, current_point = {{0,},}; |
171 | int i; |
172 | double length; |
173 | |
174 | surface = cairo_image_surface_create (format: CAIRO_FORMAT_A8, width: 0, height: 0); |
175 | cr = cairo_create (target: surface); |
176 | cairo_surface_destroy (surface); |
177 | |
178 | cairo_move_to (cr, x: x0, y: y0); |
179 | cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); |
180 | |
181 | length = 0; |
182 | path = cairo_copy_path_flat (cr); |
183 | for (i=0; i < path->num_data; i += path->data[i].header.length) { |
184 | data = &path->data[i]; |
185 | switch (data->header.type) { |
186 | |
187 | case CAIRO_PATH_MOVE_TO: |
188 | current_point = data[1]; |
189 | break; |
190 | |
191 | case CAIRO_PATH_LINE_TO: |
192 | length += two_points_distance (a: ¤t_point, b: &data[1]); |
193 | current_point = data[1]; |
194 | break; |
195 | |
196 | default: |
197 | case CAIRO_PATH_CURVE_TO: |
198 | case CAIRO_PATH_CLOSE_PATH: |
199 | g_assert_not_reached (); |
200 | } |
201 | } |
202 | cairo_path_destroy (path); |
203 | |
204 | cairo_destroy (cr); |
205 | |
206 | return length; |
207 | } |
208 | |
209 | |
210 | typedef double parametrization_t; |
211 | |
212 | /* Compute parametrization info. That is, for each part of the |
213 | * cairo path, tags it with its length. |
214 | * |
215 | * Free returned value with g_free(). |
216 | */ |
217 | static parametrization_t * |
218 | parametrize_path (cairo_path_t *path) |
219 | { |
220 | int i; |
221 | cairo_path_data_t *data, last_move_to = {{0,},}, current_point = {{0,},}; |
222 | parametrization_t *parametrization; |
223 | |
224 | parametrization = g_malloc (n_bytes: path->num_data * sizeof (parametrization[0])); |
225 | |
226 | for (i=0; i < path->num_data; i += path->data[i].header.length) { |
227 | data = &path->data[i]; |
228 | parametrization[i] = 0.0; |
229 | switch (data->header.type) { |
230 | case CAIRO_PATH_MOVE_TO: |
231 | last_move_to = data[1]; |
232 | current_point = data[1]; |
233 | break; |
234 | case CAIRO_PATH_CLOSE_PATH: |
235 | /* Make it look like it's a line_to to last_move_to */ |
236 | data = (&last_move_to) - 1; |
237 | G_GNUC_FALLTHROUGH; |
238 | case CAIRO_PATH_LINE_TO: |
239 | parametrization[i] = two_points_distance (a: ¤t_point, b: &data[1]); |
240 | current_point = data[1]; |
241 | break; |
242 | case CAIRO_PATH_CURVE_TO: |
243 | /* naive curve-length, treating bezier as three line segments: |
244 | parametrization[i] = two_points_distance (¤t_point, &data[1]) |
245 | + two_points_distance (&data[1], &data[2]) |
246 | + two_points_distance (&data[2], &data[3]); |
247 | */ |
248 | parametrization[i] = curve_length (x0: current_point.point.x, y0: current_point.point.y, |
249 | x1: data[1].point.x, y1: data[1].point.y, |
250 | x2: data[2].point.x, y2: data[2].point.y, |
251 | x3: data[3].point.x, y3: data[3].point.y); |
252 | |
253 | current_point = data[3]; |
254 | break; |
255 | default: |
256 | g_assert_not_reached (); |
257 | } |
258 | } |
259 | |
260 | return parametrization; |
261 | } |
262 | |
263 | |
264 | typedef void (*transform_point_func_t) (void *closure, double *x, double *y); |
265 | |
266 | /* Project a path using a function. Each point of the path (including |
267 | * Bezier control points) is passed to the function for transformation. |
268 | */ |
269 | static void |
270 | transform_path (cairo_path_t *path, transform_point_func_t f, void *closure) |
271 | { |
272 | int i; |
273 | cairo_path_data_t *data; |
274 | |
275 | for (i=0; i < path->num_data; i += path->data[i].header.length) { |
276 | data = &path->data[i]; |
277 | switch (data->header.type) { |
278 | case CAIRO_PATH_CURVE_TO: |
279 | f (closure, &data[3].point.x, &data[3].point.y); |
280 | f (closure, &data[2].point.x, &data[2].point.y); |
281 | G_GNUC_FALLTHROUGH; |
282 | case CAIRO_PATH_MOVE_TO: |
283 | case CAIRO_PATH_LINE_TO: |
284 | f (closure, &data[1].point.x, &data[1].point.y); |
285 | break; |
286 | case CAIRO_PATH_CLOSE_PATH: |
287 | break; |
288 | default: |
289 | g_assert_not_reached (); |
290 | } |
291 | } |
292 | } |
293 | |
294 | |
295 | /* Simple struct to hold a path and its parametrization */ |
296 | typedef struct { |
297 | cairo_path_t *path; |
298 | parametrization_t *parametrization; |
299 | } parametrized_path_t; |
300 | |
301 | |
302 | /* Project a point X,Y onto a parameterized path. The final point is |
303 | * where you get if you walk on the path forward from the beginning for X |
304 | * units, then stop there and walk another Y units perpendicular to the |
305 | * path at that point. In more detail: |
306 | * |
307 | * There's three pieces of math involved: |
308 | * |
309 | * - The parametric form of the Line equation |
310 | * http://en.wikipedia.org/wiki/Line |
311 | * |
312 | * - The parametric form of the Cubic Bézier curve equation |
313 | * http://en.wikipedia.org/wiki/B%C3%A9zier_curve |
314 | * |
315 | * - The Gradient (aka multi-dimensional derivative) of the above |
316 | * http://en.wikipedia.org/wiki/Gradient |
317 | * |
318 | * The parametric forms are used to answer the question of "where will I be |
319 | * if I walk a distance of X on this path". The Gradient is used to answer |
320 | * the question of "where will I be if then I stop, rotate left for 90 |
321 | * degrees and walk straight for a distance of Y". |
322 | */ |
323 | static void |
324 | point_on_path (parametrized_path_t *param, |
325 | double *x, double *y) |
326 | { |
327 | int i; |
328 | double ratio, the_y = *y, the_x = *x, dx, dy; |
329 | cairo_path_data_t *data, last_move_to = {{0,},}, current_point = {{0,},}; |
330 | cairo_path_t *path = param->path; |
331 | parametrization_t *parametrization = param->parametrization; |
332 | |
333 | for (i=0; i + path->data[i].header.length < path->num_data && |
334 | (the_x > parametrization[i] || |
335 | path->data[i].header.type == CAIRO_PATH_MOVE_TO); |
336 | i += path->data[i].header.length) { |
337 | the_x -= parametrization[i]; |
338 | data = &path->data[i]; |
339 | switch (data->header.type) { |
340 | case CAIRO_PATH_MOVE_TO: |
341 | current_point = data[1]; |
342 | last_move_to = data[1]; |
343 | break; |
344 | case CAIRO_PATH_LINE_TO: |
345 | current_point = data[1]; |
346 | break; |
347 | case CAIRO_PATH_CURVE_TO: |
348 | current_point = data[3]; |
349 | break; |
350 | case CAIRO_PATH_CLOSE_PATH: |
351 | break; |
352 | default: |
353 | g_assert_not_reached (); |
354 | } |
355 | } |
356 | data = &path->data[i]; |
357 | |
358 | switch (data->header.type) { |
359 | |
360 | case CAIRO_PATH_MOVE_TO: |
361 | break; |
362 | case CAIRO_PATH_CLOSE_PATH: |
363 | /* Make it look like it's a line_to to last_move_to */ |
364 | data = (&last_move_to) - 1; |
365 | G_GNUC_FALLTHROUGH; |
366 | case CAIRO_PATH_LINE_TO: |
367 | { |
368 | ratio = the_x / parametrization[i]; |
369 | /* Line polynomial */ |
370 | *x = current_point.point.x * (1 - ratio) + data[1].point.x * ratio; |
371 | *y = current_point.point.y * (1 - ratio) + data[1].point.y * ratio; |
372 | |
373 | /* Line gradient */ |
374 | dx = -(current_point.point.x - data[1].point.x); |
375 | dy = -(current_point.point.y - data[1].point.y); |
376 | |
377 | /*optimization for: ratio = the_y / sqrt (dx * dx + dy * dy);*/ |
378 | ratio = the_y / parametrization[i]; |
379 | *x += -dy * ratio; |
380 | *y += dx * ratio; |
381 | } |
382 | break; |
383 | case CAIRO_PATH_CURVE_TO: |
384 | { |
385 | /* FIXME the formulas here are not exactly what we want, because the |
386 | * Bezier parametrization is not uniform. But I don't know how to do |
387 | * better. The caller can do slightly better though, by flattening the |
388 | * Bezier and avoiding this branch completely. That has its own cost |
389 | * though, as large y values magnify the flattening error drastically. |
390 | */ |
391 | |
392 | double ratio_1_0, ratio_0_1; |
393 | double ratio_2_0, ratio_0_2; |
394 | double ratio_3_0, ratio_2_1, ratio_1_2, ratio_0_3; |
395 | double _1__4ratio_1_0_3ratio_2_0, _2ratio_1_0_3ratio_2_0; |
396 | |
397 | ratio = the_x / parametrization[i]; |
398 | |
399 | ratio_1_0 = ratio; |
400 | ratio_0_1 = 1 - ratio; |
401 | |
402 | ratio_2_0 = ratio_1_0 * ratio_1_0; /* ratio * ratio */ |
403 | ratio_0_2 = ratio_0_1 * ratio_0_1; /* (1 - ratio) * (1 - ratio) */ |
404 | |
405 | ratio_3_0 = ratio_2_0 * ratio_1_0; /* ratio * ratio * ratio */ |
406 | ratio_2_1 = ratio_2_0 * ratio_0_1; /* ratio * ratio * (1 - ratio) */ |
407 | ratio_1_2 = ratio_1_0 * ratio_0_2; /* ratio * (1 - ratio) * (1 - ratio) */ |
408 | ratio_0_3 = ratio_0_1 * ratio_0_2; /* (1 - ratio) * (1 - ratio) * (1 - ratio) */ |
409 | |
410 | _1__4ratio_1_0_3ratio_2_0 = 1 - 4 * ratio_1_0 + 3 * ratio_2_0; |
411 | _2ratio_1_0_3ratio_2_0 = 2 * ratio_1_0 - 3 * ratio_2_0; |
412 | |
413 | /* Bezier polynomial */ |
414 | *x = current_point.point.x * ratio_0_3 |
415 | + 3 * data[1].point.x * ratio_1_2 |
416 | + 3 * data[2].point.x * ratio_2_1 |
417 | + data[3].point.x * ratio_3_0; |
418 | *y = current_point.point.y * ratio_0_3 |
419 | + 3 * data[1].point.y * ratio_1_2 |
420 | + 3 * data[2].point.y * ratio_2_1 |
421 | + data[3].point.y * ratio_3_0; |
422 | |
423 | /* Bezier gradient */ |
424 | dx =-3 * current_point.point.x * ratio_0_2 |
425 | + 3 * data[1].point.x * _1__4ratio_1_0_3ratio_2_0 |
426 | + 3 * data[2].point.x * _2ratio_1_0_3ratio_2_0 |
427 | + 3 * data[3].point.x * ratio_2_0; |
428 | dy =-3 * current_point.point.y * ratio_0_2 |
429 | + 3 * data[1].point.y * _1__4ratio_1_0_3ratio_2_0 |
430 | + 3 * data[2].point.y * _2ratio_1_0_3ratio_2_0 |
431 | + 3 * data[3].point.y * ratio_2_0; |
432 | |
433 | ratio = the_y / sqrt (x: dx * dx + dy * dy); |
434 | *x += -dy * ratio; |
435 | *y += dx * ratio; |
436 | } |
437 | break; |
438 | default: |
439 | g_assert_not_reached (); |
440 | } |
441 | } |
442 | |
443 | /* Projects the current path of cr onto the provided path. */ |
444 | static void |
445 | map_path_onto (cairo_t *cr, cairo_path_t *path) |
446 | { |
447 | cairo_path_t *current_path; |
448 | parametrized_path_t param; |
449 | |
450 | param.path = path; |
451 | param.parametrization = parametrize_path (path); |
452 | |
453 | current_path = cairo_copy_path (cr); |
454 | cairo_new_path (cr); |
455 | |
456 | transform_path (path: current_path, |
457 | f: (transform_point_func_t) point_on_path, closure: ¶m); |
458 | |
459 | cairo_append_path (cr, path: current_path); |
460 | |
461 | cairo_path_destroy (path: current_path); |
462 | g_free (mem: param.parametrization); |
463 | } |
464 | |
465 | |
466 | typedef void (*draw_path_func_t) (cairo_t *cr); |
467 | |
468 | static void |
469 | draw_text (cairo_t *cr, |
470 | double x, |
471 | double y, |
472 | const char *font, |
473 | const char *text) |
474 | { |
475 | PangoLayout *layout; |
476 | PangoLayoutLine *line; |
477 | PangoFontDescription *desc; |
478 | cairo_font_options_t *font_options; |
479 | |
480 | font_options = cairo_font_options_create (); |
481 | |
482 | cairo_font_options_set_hint_style (options: font_options, hint_style: CAIRO_HINT_STYLE_NONE); |
483 | cairo_font_options_set_hint_metrics (options: font_options, hint_metrics: CAIRO_HINT_METRICS_OFF); |
484 | |
485 | cairo_set_font_options (cr, options: font_options); |
486 | cairo_font_options_destroy (options: font_options); |
487 | |
488 | layout = pango_cairo_create_layout (cr); |
489 | |
490 | desc = pango_font_description_from_string (str: font); |
491 | pango_layout_set_font_description (layout, desc); |
492 | pango_font_description_free (desc); |
493 | |
494 | pango_layout_set_text (layout, text, length: -1); |
495 | |
496 | /* Use pango_layout_get_line() instead of pango_layout_get_line_readonly() |
497 | * for older versions of pango |
498 | */ |
499 | line = pango_layout_get_line_readonly (layout, line: 0); |
500 | |
501 | cairo_move_to (cr, x, y); |
502 | pango_cairo_layout_line_path (cr, line); |
503 | |
504 | g_object_unref (object: layout); |
505 | } |
506 | |
507 | static void |
508 | draw_twisted (cairo_t *cr, |
509 | double x, |
510 | double y, |
511 | const char *font, |
512 | const char *text) |
513 | { |
514 | cairo_path_t *path; |
515 | |
516 | cairo_save (cr); |
517 | |
518 | /* Decrease tolerance a bit, since it's going to be magnified */ |
519 | cairo_set_tolerance (cr, tolerance: 0.01); |
520 | |
521 | /* Using cairo_copy_path() here shows our deficiency in handling |
522 | * Bezier curves, specially around sharper curves. |
523 | * |
524 | * Using cairo_copy_path_flat() on the other hand, magnifies the |
525 | * flattening error with large off-path values. We decreased |
526 | * tolerance for that reason. Increase tolerance to see that |
527 | * artifact. |
528 | */ |
529 | path = cairo_copy_path_flat (cr); |
530 | /*path = cairo_copy_path (cr);*/ |
531 | |
532 | cairo_new_path (cr); |
533 | |
534 | draw_text (cr, x, y, font, text); |
535 | map_path_onto (cr, path); |
536 | |
537 | cairo_path_destroy (path); |
538 | |
539 | cairo_fill_preserve (cr); |
540 | |
541 | cairo_save (cr); |
542 | cairo_set_source_rgb (cr, red: 0.1, green: 0.1, blue: 0.1); |
543 | cairo_stroke (cr); |
544 | cairo_restore (cr); |
545 | |
546 | cairo_restore (cr); |
547 | } |
548 | |
549 | static void |
550 | draw_dream (cairo_t *cr) |
551 | { |
552 | cairo_move_to (cr, x: 50, y: 650); |
553 | |
554 | cairo_rel_line_to (cr, dx: 250, dy: 50); |
555 | cairo_rel_curve_to (cr, dx1: 250, dy1: 50, dx2: 600, dy2: -50, dx3: 600, dy3: -250); |
556 | cairo_rel_curve_to (cr, dx1: 0, dy1: -400, dx2: -300, dy2: -100, dx3: -800, dy3: -300); |
557 | |
558 | cairo_set_line_width (cr, width: 1.5); |
559 | cairo_set_source_rgba (cr, red: 0.3, green: 0.3, blue: 1.0, alpha: 0.3); |
560 | |
561 | fancy_cairo_stroke_preserve (cr); |
562 | |
563 | draw_twisted (cr, |
564 | x: 0, y: 0, |
565 | font: "Serif 72" , |
566 | text: "It was a dream... Oh Just a dream..." ); |
567 | } |
568 | |
569 | static void |
570 | draw_wow (cairo_t *cr) |
571 | { |
572 | cairo_move_to (cr, x: 400, y: 780); |
573 | |
574 | cairo_rel_curve_to (cr, dx1: 50, dy1: -50, dx2: 150, dy2: -50, dx3: 200, dy3: 0); |
575 | |
576 | cairo_scale (cr, sx: 1.0, sy: 2.0); |
577 | cairo_set_line_width (cr, width: 2.0); |
578 | cairo_set_source_rgba (cr, red: 0.3, green: 1.0, blue: 0.3, alpha: 1.0); |
579 | |
580 | fancy_cairo_stroke_preserve (cr); |
581 | |
582 | draw_twisted (cr, |
583 | x: -20, y: -150, |
584 | font: "Serif 60" , |
585 | text: "WOW!" ); |
586 | } |
587 | |
588 | int main (int argc, char **argv) |
589 | { |
590 | cairo_t *cr; |
591 | char *filename; |
592 | cairo_status_t status; |
593 | cairo_surface_t *surface; |
594 | |
595 | if (argc != 2) |
596 | { |
597 | g_printerr (format: "Usage: cairotwisted OUTPUT_FILENAME\n" ); |
598 | return 1; |
599 | } |
600 | |
601 | filename = argv[1]; |
602 | |
603 | surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, |
604 | width: 1000, height: 800); |
605 | cr = cairo_create (target: surface); |
606 | |
607 | cairo_set_source_rgb (cr, red: 1.0, green: 1.0, blue: 1.0); |
608 | cairo_paint (cr); |
609 | |
610 | draw_dream (cr); |
611 | draw_wow (cr); |
612 | |
613 | cairo_destroy (cr); |
614 | |
615 | status = cairo_surface_write_to_png (surface, filename); |
616 | cairo_surface_destroy (surface); |
617 | |
618 | if (status != CAIRO_STATUS_SUCCESS) |
619 | { |
620 | g_printerr (format: "Could not save png to '%s'\n" , filename); |
621 | return 1; |
622 | } |
623 | |
624 | return 0; |
625 | } |
626 | |