1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /*
3 * Copyright © 2004,2006 Red Hat, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software
6 * and its documentation for any purpose is hereby granted without
7 * fee, provided that the above copyright notice appear in all copies
8 * and that both that copyright notice and this permission notice
9 * appear in supporting documentation, and that the name of
10 * Red Hat, Inc. not be used in advertising or publicity pertaining to
11 * distribution of the software without specific, written prior
12 * permission. Red Hat, Inc. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as
14 * is" without express or implied warranty.
15 *
16 * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18 * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
19 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
20 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
22 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Author: Carl D. Worth <cworth@cworth.org>
25 */
26
27 #include "cairo-boilerplate-private.h"
28 #include "cairo-boilerplate-scaled-font.h"
29
30 #include <pixman.h>
31
32 #include <cairo-types-private.h>
33 #include <cairo-scaled-font-private.h>
34
35 #if CAIRO_HAS_SCRIPT_SURFACE
36 #include <cairo-script.h>
37 #endif
38
39 #include <stddef.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <assert.h>
43 #include <errno.h>
44
45 #if HAVE_DLFCN_H
46 #include <dlfcn.h>
47 #endif
48
49 #if HAVE_UNISTD_H && HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_UN_H
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <signal.h>
53 #include <sys/stat.h>
54 #include <sys/socket.h>
55 #include <sys/un.h>
56
57 #define HAS_DAEMON 1
58 #define SOCKET_PATH "./.any2ppm"
59 #endif
60
61 cairo_content_t
cairo_boilerplate_content(cairo_content_t content)62 cairo_boilerplate_content (cairo_content_t content)
63 {
64 if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
65 content = CAIRO_CONTENT_COLOR_ALPHA;
66
67 return content;
68 }
69
70 const char *
cairo_boilerplate_content_name(cairo_content_t content)71 cairo_boilerplate_content_name (cairo_content_t content)
72 {
73 /* For the purpose of the content name, we don't distinguish the
74 * flattened content value.
75 */
76 switch (cairo_boilerplate_content (content)) {
77 case CAIRO_CONTENT_COLOR:
78 return "rgb24";
79 case CAIRO_CONTENT_COLOR_ALPHA:
80 return "argb32";
81 case CAIRO_CONTENT_ALPHA:
82 default:
83 assert (0); /* not reached */
84 return "---";
85 }
86 }
87
88 static const char *
_cairo_boilerplate_content_visible_name(cairo_content_t content)89 _cairo_boilerplate_content_visible_name (cairo_content_t content)
90 {
91 switch (cairo_boilerplate_content (content)) {
92 case CAIRO_CONTENT_COLOR:
93 return "rgb";
94 case CAIRO_CONTENT_COLOR_ALPHA:
95 return "rgba";
96 case CAIRO_CONTENT_ALPHA:
97 return "a";
98 default:
99 assert (0); /* not reached */
100 return "---";
101 }
102 }
103
104 cairo_format_t
cairo_boilerplate_format_from_content(cairo_content_t content)105 cairo_boilerplate_format_from_content (cairo_content_t content)
106 {
107 cairo_format_t format;
108
109 switch (content) {
110 case CAIRO_CONTENT_COLOR:
111 format = CAIRO_FORMAT_RGB24;
112 break;
113 case CAIRO_CONTENT_COLOR_ALPHA:
114 format = CAIRO_FORMAT_ARGB32;
115 break;
116 case CAIRO_CONTENT_ALPHA:
117 format = CAIRO_FORMAT_A8;
118 break;
119 default:
120 assert (0); /* not reached */
121 format = CAIRO_FORMAT_INVALID;
122 break;
123 }
124
125 return format;
126 }
127
128 static cairo_surface_t *
_cairo_boilerplate_image_create_surface(const char * name,cairo_content_t content,double width,double height,double max_width,double max_height,cairo_boilerplate_mode_t mode,void ** closure)129 _cairo_boilerplate_image_create_surface (const char *name,
130 cairo_content_t content,
131 double width,
132 double height,
133 double max_width,
134 double max_height,
135 cairo_boilerplate_mode_t mode,
136 void **closure)
137 {
138 cairo_format_t format;
139
140 *closure = NULL;
141
142 if (content == CAIRO_CONTENT_COLOR_ALPHA) {
143 format = CAIRO_FORMAT_ARGB32;
144 } else if (content == CAIRO_CONTENT_COLOR) {
145 format = CAIRO_FORMAT_RGB24;
146 } else {
147 assert (0); /* not reached */
148 return NULL;
149 }
150
151 return cairo_image_surface_create (format, ceil (width), ceil (height));
152 }
153
154 static const cairo_user_data_key_t key;
155
156 static cairo_surface_t *
_cairo_boilerplate_image_create_similar(cairo_surface_t * other,cairo_content_t content,int width,int height)157 _cairo_boilerplate_image_create_similar (cairo_surface_t *other,
158 cairo_content_t content,
159 int width, int height)
160 {
161 cairo_format_t format;
162 cairo_surface_t *surface;
163 int stride;
164 void *ptr;
165
166 switch (content) {
167 case CAIRO_CONTENT_ALPHA:
168 format = CAIRO_FORMAT_A8;
169 break;
170 case CAIRO_CONTENT_COLOR:
171 format = CAIRO_FORMAT_RGB24;
172 break;
173 case CAIRO_CONTENT_COLOR_ALPHA:
174 default:
175 format = CAIRO_FORMAT_ARGB32;
176 break;
177 }
178
179 stride = cairo_format_stride_for_width(format, width);
180 ptr = malloc (stride* height);
181
182 surface = cairo_image_surface_create_for_data (ptr, format,
183 width, height, stride);
184 cairo_surface_set_user_data (surface, &key, ptr, free);
185
186 return surface;
187 }
188
189 static cairo_surface_t *
_cairo_boilerplate_image16_create_surface(const char * name,cairo_content_t content,double width,double height,double max_width,double max_height,cairo_boilerplate_mode_t mode,void ** closure)190 _cairo_boilerplate_image16_create_surface (const char *name,
191 cairo_content_t content,
192 double width,
193 double height,
194 double max_width,
195 double max_height,
196 cairo_boilerplate_mode_t mode,
197 void **closure)
198 {
199 *closure = NULL;
200
201 /* XXX force CAIRO_CONTENT_COLOR */
202 return cairo_image_surface_create (CAIRO_FORMAT_RGB16_565, ceil (width), ceil (height));
203 }
204
205 static cairo_surface_t *
_cairo_boilerplate_image16_create_similar(cairo_surface_t * other,cairo_content_t content,int width,int height)206 _cairo_boilerplate_image16_create_similar (cairo_surface_t *other,
207 cairo_content_t content,
208 int width, int height)
209 {
210 cairo_format_t format;
211 cairo_surface_t *surface;
212 int stride;
213 void *ptr;
214
215 switch (content) {
216 case CAIRO_CONTENT_ALPHA:
217 format = CAIRO_FORMAT_A8;
218 break;
219 case CAIRO_CONTENT_COLOR:
220 format = CAIRO_FORMAT_RGB16_565;
221 break;
222 case CAIRO_CONTENT_COLOR_ALPHA:
223 default:
224 format = CAIRO_FORMAT_ARGB32;
225 break;
226 }
227
228 stride = cairo_format_stride_for_width(format, width);
229 ptr = malloc (stride* height);
230
231 surface = cairo_image_surface_create_for_data (ptr, format,
232 width, height, stride);
233 cairo_surface_set_user_data (surface, &key, ptr, free);
234
235 return surface;
236 }
237
238 static char *
_cairo_boilerplate_image_describe(void * closure)239 _cairo_boilerplate_image_describe (void *closure)
240 {
241 char *s;
242
243 xasprintf (&s, "pixman %s", pixman_version_string ());
244
245 return s;
246 }
247
248 #if CAIRO_HAS_RECORDING_SURFACE
249 static cairo_surface_t *
_cairo_boilerplate_recording_create_surface(const char * name,cairo_content_t content,double width,double height,double max_width,double max_height,cairo_boilerplate_mode_t mode,void ** closure)250 _cairo_boilerplate_recording_create_surface (const char *name,
251 cairo_content_t content,
252 double width,
253 double height,
254 double max_width,
255 double max_height,
256 cairo_boilerplate_mode_t mode,
257 void **closure)
258 {
259 cairo_rectangle_t extents;
260
261 extents.x = 0;
262 extents.y = 0;
263 extents.width = width;
264 extents.height = height;
265 return *closure = cairo_surface_reference (cairo_recording_surface_create (content, &extents));
266 }
267
268 static void
_cairo_boilerplate_recording_surface_cleanup(void * closure)269 _cairo_boilerplate_recording_surface_cleanup (void *closure)
270 {
271 cairo_surface_finish (closure);
272 cairo_surface_destroy (closure);
273 }
274 #endif
275
276 const cairo_user_data_key_t cairo_boilerplate_output_basename_key;
277
278 cairo_surface_t *
_cairo_boilerplate_get_image_surface(cairo_surface_t * src,int page,int width,int height)279 _cairo_boilerplate_get_image_surface (cairo_surface_t *src,
280 int page,
281 int width,
282 int height)
283 {
284 cairo_surface_t *surface, *image;
285 cairo_t *cr;
286 cairo_status_t status;
287 cairo_format_t format;
288
289 if (cairo_surface_status (src))
290 return cairo_surface_reference (src);
291
292 if (page != 0)
293 return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
294
295 /* extract sub-surface */
296 switch (cairo_surface_get_content (src)) {
297 case CAIRO_CONTENT_ALPHA:
298 format = CAIRO_FORMAT_A8;
299 break;
300 case CAIRO_CONTENT_COLOR:
301 format = CAIRO_FORMAT_RGB24;
302 break;
303 default:
304 case CAIRO_CONTENT_COLOR_ALPHA:
305 format = CAIRO_FORMAT_ARGB32;
306 break;
307 }
308 surface = cairo_image_surface_create (format, width, height);
309 assert (cairo_surface_get_content (surface) == cairo_surface_get_content (src));
310 image = cairo_surface_reference (surface);
311
312 /* open a logging channel (only interesting for recording surfaces) */
313 #if CAIRO_HAS_SCRIPT_SURFACE && CAIRO_HAS_RECORDING_SURFACE
314 if (cairo_surface_get_type (src) == CAIRO_SURFACE_TYPE_RECORDING) {
315 const char *test_name;
316
317 test_name = cairo_surface_get_user_data (src,
318 &cairo_boilerplate_output_basename_key);
319 if (test_name != NULL) {
320 cairo_device_t *ctx;
321 char *filename;
322
323 cairo_surface_destroy (surface);
324
325 xasprintf (&filename, "%s.out.trace", test_name);
326 ctx = cairo_script_create (filename);
327 surface = cairo_script_surface_create_for_target (ctx, image);
328 cairo_device_destroy (ctx);
329 free (filename);
330 }
331 }
332 #endif
333
334 cr = cairo_create (surface);
335 cairo_surface_destroy (surface);
336 cairo_set_source_surface (cr, src, 0, 0);
337 cairo_paint (cr);
338
339 status = cairo_status (cr);
340 if (status) {
341 cairo_surface_destroy (image);
342 image = cairo_surface_reference (cairo_get_target (cr));
343 }
344 cairo_destroy (cr);
345
346 return image;
347 }
348
349 cairo_surface_t *
cairo_boilerplate_get_image_surface_from_png(const char * filename,int width,int height,cairo_bool_t flatten)350 cairo_boilerplate_get_image_surface_from_png (const char *filename,
351 int width,
352 int height,
353 cairo_bool_t flatten)
354 {
355 cairo_surface_t *surface;
356
357 surface = cairo_image_surface_create_from_png (filename);
358 if (cairo_surface_status (surface))
359 return surface;
360
361 if (flatten) {
362 cairo_t *cr;
363 cairo_surface_t *flattened;
364
365 flattened = cairo_image_surface_create (cairo_image_surface_get_format (surface),
366 width,
367 height);
368 cr = cairo_create (flattened);
369 cairo_surface_destroy (flattened);
370
371 cairo_set_source_rgb (cr, 1, 1, 1);
372 cairo_paint (cr);
373
374 cairo_set_source_surface (cr, surface,
375 width - cairo_image_surface_get_width (surface),
376 height - cairo_image_surface_get_height (surface));
377 cairo_paint (cr);
378
379 cairo_surface_destroy (surface);
380 surface = cairo_surface_reference (cairo_get_target (cr));
381 cairo_destroy (cr);
382 } else if (cairo_image_surface_get_width (surface) != width ||
383 cairo_image_surface_get_height (surface) != height)
384 {
385 cairo_t *cr;
386 cairo_surface_t *sub;
387
388 sub = cairo_image_surface_create (cairo_image_surface_get_format (surface),
389 width,
390 height);
391 cr = cairo_create (sub);
392 cairo_surface_destroy (sub);
393
394 cairo_set_source_surface (cr, surface,
395 width - cairo_image_surface_get_width (surface),
396 height - cairo_image_surface_get_height (surface));
397 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
398 cairo_paint (cr);
399
400 cairo_surface_destroy (surface);
401 surface = cairo_surface_reference (cairo_get_target (cr));
402 cairo_destroy (cr);
403 }
404
405 return surface;
406 }
407
408 static const cairo_boilerplate_target_t builtin_targets[] = {
409 /* I'm uncompromising about leaving the image backend as 0
410 * for tolerance. There shouldn't ever be anything that is out of
411 * our control here. */
412 {
413 "image", "image", NULL, NULL,
414 CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, 0,
415 NULL,
416 _cairo_boilerplate_image_create_surface,
417 _cairo_boilerplate_image_create_similar,
418 NULL, NULL,
419 _cairo_boilerplate_get_image_surface,
420 cairo_surface_write_to_png,
421 NULL, NULL,
422 _cairo_boilerplate_image_describe,
423 TRUE, FALSE, FALSE
424 },
425 {
426 "image", "image", NULL, NULL,
427 CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
428 NULL,
429 _cairo_boilerplate_image_create_surface,
430 _cairo_boilerplate_image_create_similar,
431 NULL, NULL,
432 _cairo_boilerplate_get_image_surface,
433 cairo_surface_write_to_png,
434 NULL, NULL,
435 _cairo_boilerplate_image_describe,
436 FALSE, FALSE, FALSE
437 },
438 {
439 "image16", "image", NULL, NULL,
440 CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
441 NULL,
442 _cairo_boilerplate_image16_create_surface,
443 _cairo_boilerplate_image16_create_similar,
444 NULL, NULL,
445 _cairo_boilerplate_get_image_surface,
446 cairo_surface_write_to_png,
447 NULL, NULL,
448 _cairo_boilerplate_image_describe,
449 TRUE, FALSE, FALSE
450 },
451 #if CAIRO_HAS_RECORDING_SURFACE
452 {
453 "recording", "image", NULL, NULL,
454 CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR_ALPHA, 0,
455 "cairo_recording_surface_create",
456 _cairo_boilerplate_recording_create_surface,
457 cairo_surface_create_similar,
458 NULL, NULL,
459 _cairo_boilerplate_get_image_surface,
460 cairo_surface_write_to_png,
461 _cairo_boilerplate_recording_surface_cleanup,
462 NULL, NULL,
463 FALSE, FALSE, TRUE
464 },
465 {
466 "recording", "image", NULL, NULL,
467 CAIRO_SURFACE_TYPE_RECORDING, CAIRO_CONTENT_COLOR, 0,
468 "cairo_recording_surface_create",
469 _cairo_boilerplate_recording_create_surface,
470 cairo_surface_create_similar,
471 NULL, NULL,
472 _cairo_boilerplate_get_image_surface,
473 cairo_surface_write_to_png,
474 _cairo_boilerplate_recording_surface_cleanup,
475 NULL, NULL,
476 FALSE, FALSE, TRUE
477 },
478 #endif
479 };
480 CAIRO_BOILERPLATE (builtin, builtin_targets)
481
482 static struct cairo_boilerplate_target_list {
483 struct cairo_boilerplate_target_list *next;
484 const cairo_boilerplate_target_t *target;
485 } *cairo_boilerplate_targets;
486
487 static cairo_bool_t
probe_target(const cairo_boilerplate_target_t * target)488 probe_target (const cairo_boilerplate_target_t *target)
489 {
490 if (target->probe == NULL)
491 return TRUE;
492
493 #if HAVE_DLSYM
494 return dlsym (NULL, target->probe) != NULL;
495 #else
496 return TRUE;
497 #endif
498 }
499
500 void
_cairo_boilerplate_register_backend(const cairo_boilerplate_target_t * targets,unsigned int count)501 _cairo_boilerplate_register_backend (const cairo_boilerplate_target_t *targets,
502 unsigned int count)
503 {
504 targets += count;
505 while (count--) {
506 struct cairo_boilerplate_target_list *list;
507
508 --targets;
509 if (! probe_target (targets))
510 continue;
511
512 list = xmalloc (sizeof (*list));
513 list->next = cairo_boilerplate_targets;
514 list->target = targets;
515 cairo_boilerplate_targets = list;
516 }
517 }
518
519 static cairo_bool_t
_cairo_boilerplate_target_format_matches_name(const cairo_boilerplate_target_t * target,const char * tcontent_name,const char * tcontent_end)520 _cairo_boilerplate_target_format_matches_name (const cairo_boilerplate_target_t *target,
521 const char *tcontent_name,
522 const char *tcontent_end)
523 {
524 char const *content_name;
525 const char *content_end = tcontent_end;
526 size_t content_len;
527
528 content_name = _cairo_boilerplate_content_visible_name (target->content);
529 if (tcontent_end)
530 content_len = content_end - tcontent_name;
531 else
532 content_len = strlen(tcontent_name);
533 if (strlen(content_name) != content_len)
534 return FALSE;
535 if (0 == strncmp (content_name, tcontent_name, content_len))
536 return TRUE;
537
538 return FALSE;
539 }
540
541 static cairo_bool_t
_cairo_boilerplate_target_matches_name(const cairo_boilerplate_target_t * target,const char * tname,const char * end)542 _cairo_boilerplate_target_matches_name (const cairo_boilerplate_target_t *target,
543 const char *tname,
544 const char *end)
545 {
546 char const *content_name;
547 const char *content_start = strpbrk (tname, ".");
548 const char *content_end = end;
549 size_t name_len;
550 size_t content_len;
551
552 if (content_start >= end)
553 content_start = NULL;
554 if (content_start != NULL)
555 end = content_start++;
556
557 name_len = end - tname;
558
559 /* Check name. */
560 if (! (name_len == 1 && 0 == strncmp (tname, "?", 1))) { /* wildcard? */
561 if (0 != strncmp (target->name, tname, name_len)) /* exact match? */
562 return FALSE;
563 if (isalnum (target->name[name_len]))
564 return FALSE;
565 }
566
567 /* Check optional content. */
568 if (content_start == NULL) /* none given? */
569 return TRUE;
570
571 /* Exact content match? */
572 content_name = _cairo_boilerplate_content_visible_name (target->content);
573 content_len = content_end - content_start;
574 if (strlen(content_name) != content_len)
575 return FALSE;
576 if (0 == strncmp (content_name, content_start, content_len))
577 return TRUE;
578
579 return FALSE;
580 }
581
582 const cairo_boilerplate_target_t **
cairo_boilerplate_get_targets(int * pnum_targets,cairo_bool_t * plimited_targets)583 cairo_boilerplate_get_targets (int *pnum_targets,
584 cairo_bool_t *plimited_targets)
585 {
586 size_t i, num_targets;
587 cairo_bool_t limited_targets = FALSE;
588 const char *tname;
589 const cairo_boilerplate_target_t **targets_to_test;
590 struct cairo_boilerplate_target_list *list;
591
592 if (cairo_boilerplate_targets == NULL)
593 _cairo_boilerplate_register_all ();
594
595 if ((tname = getenv ("CAIRO_TEST_TARGET")) != NULL && *tname) {
596 /* check the list of targets specified by the user */
597 limited_targets = TRUE;
598
599 num_targets = 0;
600 targets_to_test = NULL;
601
602 while (*tname) {
603 int found = 0;
604 const char *end = strpbrk (tname, " \t\r\n;:,");
605 if (!end)
606 end = tname + strlen (tname);
607
608 if (end == tname) {
609 tname = end + 1;
610 continue;
611 }
612
613 for (list = cairo_boilerplate_targets;
614 list != NULL;
615 list = list->next)
616 {
617 const cairo_boilerplate_target_t *target = list->target;
618 const char *tcontent_name;
619 const char *tcontent_end;
620 if (_cairo_boilerplate_target_matches_name (target, tname, end)) {
621 if ((tcontent_name = getenv ("CAIRO_TEST_TARGET_FORMAT")) != NULL && *tcontent_name) {
622 while(tcontent_name) {
623 tcontent_end = strpbrk (tcontent_name, " \t\r\n;:,");
624 if (tcontent_end == tcontent_name) {
625 tcontent_name = tcontent_end + 1;
626 continue;
627 }
628 if(_cairo_boilerplate_target_format_matches_name (target,
629 tcontent_name, tcontent_end)) {
630 /* realloc isn't exactly the best thing here, but meh. */
631 targets_to_test = xrealloc (targets_to_test,
632 sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
633 targets_to_test[num_targets++] = target;
634 found = 1;
635 }
636
637 if (tcontent_end)
638 tcontent_end++;
639 tcontent_name = tcontent_end;
640 }
641 } else {
642 /* realloc isn't exactly the best thing here, but meh. */
643 targets_to_test = xrealloc (targets_to_test,
644 sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
645 targets_to_test[num_targets++] = target;
646 found = 1;
647 }
648 }
649 }
650
651 if (!found) {
652 const char *last_name = NULL;
653
654 fprintf (stderr, "Cannot find target '%.*s'.\n",
655 (int)(end - tname), tname);
656 fprintf (stderr, "Known targets:");
657 for (list = cairo_boilerplate_targets;
658 list != NULL;
659 list = list->next)
660 {
661 const cairo_boilerplate_target_t *target = list->target;
662 if (last_name != NULL) {
663 if (strcmp (target->name, last_name) == 0) {
664 /* filter out repeats that differ in content */
665 continue;
666 }
667 fprintf (stderr, ",");
668 }
669 fprintf (stderr, " %s", target->name);
670 last_name = target->name;
671 }
672 fprintf (stderr, "\n");
673 exit(-1);
674 }
675
676 if (*end)
677 end++;
678 tname = end;
679 }
680 } else {
681 int found = 0;
682 int not_found_targets = 0;
683 num_targets = 0;
684 targets_to_test = xmalloc (sizeof(cairo_boilerplate_target_t*) * num_targets);
685 for (list = cairo_boilerplate_targets; list != NULL; list = list->next)
686 {
687 const cairo_boilerplate_target_t *target = list->target;
688 const char *tcontent_name;
689 const char *tcontent_end;
690 if ((tcontent_name = getenv ("CAIRO_TEST_TARGET_FORMAT")) != NULL && *tcontent_name) {
691 while(tcontent_name) {
692 tcontent_end = strpbrk (tcontent_name, " \t\r\n;:,");
693 if (tcontent_end == tcontent_name) {
694 tcontent_name = tcontent_end + 1;
695 continue;
696 }
697 if (_cairo_boilerplate_target_format_matches_name (target,
698 tcontent_name, tcontent_end)) {
699 /* realloc isn't exactly the best thing here, but meh. */
700 targets_to_test = xrealloc (targets_to_test,
701 sizeof(cairo_boilerplate_target_t *) * (num_targets+1));
702 targets_to_test[num_targets++] = target;
703 found =1;
704 }
705 else
706 {
707 not_found_targets++;
708 }
709
710 if (tcontent_end)
711 tcontent_end++;
712
713 tcontent_name = tcontent_end;
714 }
715 }
716 else
717 {
718 num_targets++;
719 }
720 }
721 if (!found)
722 {
723 /* check all compiled in targets */
724 num_targets = num_targets + not_found_targets;
725 targets_to_test = xrealloc (targets_to_test,
726 sizeof(cairo_boilerplate_target_t*) * num_targets);
727 num_targets = 0;
728 for (list = cairo_boilerplate_targets;
729 list != NULL;
730 list = list->next)
731 {
732 const cairo_boilerplate_target_t *target = list->target;
733 targets_to_test[num_targets++] = target;
734 }
735 }
736
737 }
738
739 /* exclude targets as specified by the user */
740 if ((tname = getenv ("CAIRO_TEST_TARGET_EXCLUDE")) != NULL && *tname) {
741 limited_targets = TRUE;
742
743 while (*tname) {
744 int j;
745 const char *end = strpbrk (tname, " \t\r\n;:,");
746 if (!end)
747 end = tname + strlen (tname);
748
749 if (end == tname) {
750 tname = end + 1;
751 continue;
752 }
753
754 for (i = j = 0; i < num_targets; i++) {
755 const cairo_boilerplate_target_t *target = targets_to_test[i];
756 if (! _cairo_boilerplate_target_matches_name (target,
757 tname, end))
758 {
759 targets_to_test[j++] = targets_to_test[i];
760 }
761 }
762 num_targets = j;
763
764 if (*end)
765 end++;
766 tname = end;
767 }
768 }
769
770 if (pnum_targets)
771 *pnum_targets = num_targets;
772
773 if (plimited_targets)
774 *plimited_targets = limited_targets;
775
776 return targets_to_test;
777 }
778
779 const cairo_boilerplate_target_t *
cairo_boilerplate_get_image_target(cairo_content_t content)780 cairo_boilerplate_get_image_target (cairo_content_t content)
781 {
782 if (cairo_boilerplate_targets == NULL)
783 _cairo_boilerplate_register_all ();
784
785 switch (content) {
786 case CAIRO_CONTENT_COLOR:
787 return &builtin_targets[1];
788 case CAIRO_CONTENT_COLOR_ALPHA:
789 return &builtin_targets[0];
790 case CAIRO_CONTENT_ALPHA:
791 default:
792 return NULL;
793 }
794 }
795
796 const cairo_boilerplate_target_t *
cairo_boilerplate_get_target_by_name(const char * name,cairo_content_t content)797 cairo_boilerplate_get_target_by_name (const char *name,
798 cairo_content_t content)
799 {
800 struct cairo_boilerplate_target_list *list;
801
802 if (cairo_boilerplate_targets == NULL)
803 _cairo_boilerplate_register_all ();
804
805 /* first return an exact match */
806 for (list = cairo_boilerplate_targets; list != NULL; list = list->next) {
807 const cairo_boilerplate_target_t *target = list->target;
808 if (strcmp (target->name, name) == 0 &&
809 target->content == content)
810 {
811 return target;
812 }
813 }
814
815 /* otherwise just return a match that may differ in content */
816 for (list = cairo_boilerplate_targets; list != NULL; list = list->next) {
817 const cairo_boilerplate_target_t *target = list->target;
818 if (strcmp (target->name, name) == 0)
819 return target;
820 }
821
822 return NULL;
823 }
824
825 void
cairo_boilerplate_free_targets(const cairo_boilerplate_target_t ** targets)826 cairo_boilerplate_free_targets (const cairo_boilerplate_target_t **targets)
827 {
828 free (targets);
829 }
830
831 cairo_surface_t *
cairo_boilerplate_surface_create_in_error(cairo_status_t status)832 cairo_boilerplate_surface_create_in_error (cairo_status_t status)
833 {
834 cairo_surface_t *surface = NULL;
835 int loop = 5;
836
837 do {
838 cairo_surface_t *intermediate;
839 cairo_t *cr;
840 cairo_path_t path;
841
842 intermediate = cairo_image_surface_create (CAIRO_FORMAT_A8, 0, 0);
843 cr = cairo_create (intermediate);
844 cairo_surface_destroy (intermediate);
845
846 path.status = status;
847 cairo_append_path (cr, &path);
848
849 cairo_surface_destroy (surface);
850 surface = cairo_surface_reference (cairo_get_target (cr));
851 cairo_destroy (cr);
852 } while (cairo_surface_status (surface) != status && --loop);
853
854 return surface;
855 }
856
857 void
cairo_boilerplate_scaled_font_set_max_glyphs_cached(cairo_scaled_font_t * scaled_font,int max_glyphs)858 cairo_boilerplate_scaled_font_set_max_glyphs_cached (cairo_scaled_font_t *scaled_font,
859 int max_glyphs)
860 {
861 /* XXX CAIRO_DEBUG */
862 }
863
864 #if HAS_DAEMON
865 static int
any2ppm_daemon_exists(void)866 any2ppm_daemon_exists (void)
867 {
868 struct stat st;
869 int fd;
870 char buf[80];
871 int pid;
872 int ret;
873
874 if (stat (SOCKET_PATH, &st) < 0)
875 return 0;
876
877 fd = open (SOCKET_PATH ".pid", O_RDONLY);
878 if (fd < 0)
879 return 0;
880
881 pid = 0;
882 ret = read (fd, buf, sizeof (buf) - 1);
883 if (ret > 0) {
884 buf[ret] = '\0';
885 pid = atoi (buf);
886 }
887 close (fd);
888
889 return pid > 0 && kill (pid, 0) != -1;
890 }
891 #endif
892
893 FILE *
cairo_boilerplate_open_any2ppm(const char * filename,int page,unsigned int flags,int (** close_cb)(FILE *))894 cairo_boilerplate_open_any2ppm (const char *filename,
895 int page,
896 unsigned int flags,
897 int (**close_cb) (FILE *))
898 {
899 char command[4096];
900 const char *any2ppm;
901 #if HAS_DAEMON
902 int sk;
903 struct sockaddr_un addr;
904 int len;
905 #endif
906
907 any2ppm = getenv ("ANY2PPM");
908 if (any2ppm == NULL)
909 any2ppm = "./any2ppm";
910
911 #if HAS_DAEMON
912 if (flags & CAIRO_BOILERPLATE_OPEN_NO_DAEMON)
913 goto POPEN;
914
915 if (! any2ppm_daemon_exists ()) {
916 if (system (any2ppm) != 0)
917 goto POPEN;
918 }
919
920 sk = socket (PF_UNIX, SOCK_STREAM, 0);
921 if (sk == -1)
922 goto POPEN;
923
924 memset (&addr, 0, sizeof (addr));
925 addr.sun_family = AF_UNIX;
926 strcpy (addr.sun_path, SOCKET_PATH);
927
928 if (connect (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
929 close (sk);
930 goto POPEN;
931 }
932
933 len = sprintf (command, "%s %d\n", filename, page);
934 if (write (sk, command, len) != len) {
935 close (sk);
936 goto POPEN;
937 }
938
939 *close_cb = fclose;
940 return fdopen (sk, "rb");
941
942 POPEN:
943 #endif
944
945 *close_cb = pclose;
946 sprintf (command, "%s %s %d", any2ppm, filename, page);
947 return popen (command, "rb");
948 }
949
950 static cairo_bool_t
freadn(unsigned char * buf,int len,FILE * file)951 freadn (unsigned char *buf,
952 int len,
953 FILE *file)
954 {
955 int ret;
956
957 while (len) {
958 ret = fread (buf, 1, len, file);
959 if (ret != len) {
960 if (ferror (file) || feof (file))
961 return FALSE;
962 }
963 len -= ret;
964 buf += len;
965 }
966
967 return TRUE;
968 }
969
970 cairo_surface_t *
cairo_boilerplate_image_surface_create_from_ppm_stream(FILE * file)971 cairo_boilerplate_image_surface_create_from_ppm_stream (FILE *file)
972 {
973 char format;
974 int width, height;
975 ptrdiff_t stride;
976 int x, y;
977 unsigned char *data;
978 cairo_surface_t *image = NULL;
979
980 if (fscanf (file, "P%c %d %d 255\n", &format, &width, &height) != 3)
981 goto FAIL;
982
983 switch (format) {
984 case '7': /* XXX */
985 image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
986 break;
987 case '6':
988 image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
989 break;
990 case '5':
991 image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
992 break;
993 default:
994 goto FAIL;
995 }
996 if (cairo_surface_status (image))
997 return image;
998
999 data = cairo_image_surface_get_data (image);
1000 stride = cairo_image_surface_get_stride (image);
1001 for (y = 0; y < height; y++) {
1002 unsigned char *buf = data + y*stride;
1003 switch (format) {
1004 case '7':
1005 if (! freadn (buf, 4 * width, file))
1006 goto FAIL;
1007 break;
1008 case '6':
1009 if (! freadn (buf, 3*width, file))
1010 goto FAIL;
1011 buf += 3*width;
1012 for (x = width; x--; ) {
1013 buf -= 3;
1014 ((uint32_t *) (data + y*stride))[x] =
1015 (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
1016 }
1017 break;
1018 case '5':
1019 if (! freadn (buf, width, file))
1020 goto FAIL;
1021 break;
1022 }
1023 }
1024 cairo_surface_mark_dirty (image);
1025
1026 return image;
1027
1028 FAIL:
1029 cairo_surface_destroy (image);
1030 return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
1031 }
1032
1033 cairo_surface_t *
cairo_boilerplate_convert_to_image(const char * filename,int page)1034 cairo_boilerplate_convert_to_image (const char *filename,
1035 int page)
1036 {
1037 FILE *file;
1038 unsigned int flags = 0;
1039 cairo_surface_t *image;
1040 int (*close_cb) (FILE *);
1041 int ret;
1042
1043 RETRY:
1044 file = cairo_boilerplate_open_any2ppm (filename, page, flags, &close_cb);
1045 if (file == NULL) {
1046 switch (errno) {
1047 case ENOMEM:
1048 return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
1049 default:
1050 return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
1051 }
1052 }
1053
1054 image = cairo_boilerplate_image_surface_create_from_ppm_stream (file);
1055 ret = close_cb (file);
1056 /* check for fatal errors from the interpreter */
1057 if (ret) { /* any2pmm should never die... */
1058 cairo_surface_destroy (image);
1059 return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_INVALID_STATUS);
1060 }
1061
1062 if (ret == 0 && cairo_surface_status (image) == CAIRO_STATUS_READ_ERROR) {
1063 if (flags == 0) {
1064 /* Try again in a standalone process. */
1065 cairo_surface_destroy (image);
1066 flags = CAIRO_BOILERPLATE_OPEN_NO_DAEMON;
1067 goto RETRY;
1068 }
1069 }
1070
1071 return image;
1072 }
1073
1074 int
cairo_boilerplate_version(void)1075 cairo_boilerplate_version (void)
1076 {
1077 return CAIRO_VERSION;
1078 }
1079
1080 const char*
cairo_boilerplate_version_string(void)1081 cairo_boilerplate_version_string (void)
1082 {
1083 return CAIRO_VERSION_STRING;
1084 }
1085
1086 void
cairo_boilerplate_fini(void)1087 cairo_boilerplate_fini (void)
1088 {
1089 while (cairo_boilerplate_targets != NULL) {
1090 struct cairo_boilerplate_target_list *next;
1091
1092 next = cairo_boilerplate_targets->next;
1093
1094 free (cairo_boilerplate_targets);
1095 cairo_boilerplate_targets = next;
1096 }
1097 }
1098