summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authormh0310.choi <mh0310.choi@samsung.com>2015-07-28 10:46:57 +0900
committermh0310.choi <mh0310.choi@samsung.com>2015-07-28 13:08:12 +0900
commit5e67a6f721eaedda61300baf0799199c7771ebd0 (patch)
tree6cd50b52498aab50e79b966cdccc2a137db316d9 /util
parentd3aeffba37161d2b76b29c4ea13369bd67a47a8e (diff)
downloadcairo-5e67a6f721eaedda61300baf0799199c7771ebd0.tar.gz
cairo-5e67a6f721eaedda61300baf0799199c7771ebd0.tar.bz2
cairo-5e67a6f721eaedda61300baf0799199c7771ebd0.zip
- from 1.12.14 to 1.14.2 Change-Id: I3b62d212041b337bbb926d579f9ce74f42a45c3b
Diffstat (limited to 'util')
-rw-r--r--[-rwxr-xr-x]util/.gitignore0
-rw-r--r--[-rwxr-xr-x]util/COPYING0
-rw-r--r--[-rwxr-xr-x]util/Makefile.am3
-rw-r--r--[-rwxr-xr-x]util/README0
-rw-r--r--util/backtrace-symbols.c377
-rw-r--r--util/cairo-fdr/Makefile.am15
-rw-r--r--util/cairo-fdr/fdr.c331
-rw-r--r--[-rwxr-xr-x]util/cairo-gobject/Makefile.am0
-rw-r--r--[-rwxr-xr-x]util/cairo-gobject/cairo-gobject-enums.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-gobject/cairo-gobject-structs.c4
-rw-r--r--[-rwxr-xr-x]util/cairo-gobject/cairo-gobject.h4
-rw-r--r--[-rwxr-xr-x]util/cairo-missing/Makefile.am0
-rw-r--r--[-rwxr-xr-x]util/cairo-missing/Makefile.sources0
-rw-r--r--[-rwxr-xr-x]util/cairo-missing/Makefile.win320
-rw-r--r--[-rwxr-xr-x]util/cairo-missing/cairo-missing.h2
-rw-r--r--[-rwxr-xr-x]util/cairo-missing/getline.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-missing/strndup.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/.gitignore0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/COPYING0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/Makefile.am0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/Makefile.sources0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/Makefile.win320
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-file.c9
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-hash.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-interpreter.c2
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-interpreter.h0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-objects.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-operators.c76
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-private.h0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-scanner.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/cairo-script-stack.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/csi-bind.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/csi-exec.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/csi-replay.c0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/csi-trace.c10
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/Makefile.am0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/dragon.cs0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/hilbert.cs0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/infinichess.cs0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/interference.cs0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/pythagoras-tree.cs0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/sierpinski.cs0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/wedgeAnnulus_crop_ybRings.cs0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/world-map.cs0
-rw-r--r--[-rwxr-xr-x]util/cairo-script/examples/zrusin.cs0
-rw-r--r--util/cairo-sphinx/.gitignore1
-rw-r--r--util/cairo-sphinx/Makefile.am43
-rw-r--r--util/cairo-sphinx/fdr.c261
-rw-r--r--util/cairo-sphinx/sphinx.c1545
-rw-r--r--util/cairo-trace/.gitignore1
-rw-r--r--util/cairo-trace/COPYING5
-rw-r--r--util/cairo-trace/COPYING-GPL-3674
-rw-r--r--util/cairo-trace/Makefile.am40
-rw-r--r--util/cairo-trace/cairo-trace.in136
-rw-r--r--util/cairo-trace/lookup-symbol.c331
-rw-r--r--util/cairo-trace/lookup-symbol.h24
-rw-r--r--util/cairo-trace/trace.c5581
-rw-r--r--[-rwxr-xr-x]util/cairo.modules0
-rw-r--r--[-rwxr-xr-x]util/font-view.c0
-rw-r--r--[-rwxr-xr-x]util/malloc-stats.c9
-rw-r--r--[-rwxr-xr-x]util/show-contour.c0
-rw-r--r--[-rwxr-xr-x]util/show-edges.c0
-rw-r--r--[-rwxr-xr-x]util/show-events.c0
-rw-r--r--[-rwxr-xr-x]util/show-polygon.c0
-rw-r--r--[-rwxr-xr-x]util/show-traps.c0
-rw-r--r--[-rwxr-xr-x]util/trace-to-xml.c0
-rw-r--r--[-rwxr-xr-x]util/xml-to-trace.c0
67 files changed, 9463 insertions, 21 deletions
diff --git a/util/.gitignore b/util/.gitignore
index 9a28da1b9..9a28da1b9 100755..100644
--- a/util/.gitignore
+++ b/util/.gitignore
diff --git a/util/COPYING b/util/COPYING
index ea44bb6f5..ea44bb6f5 100755..100644
--- a/util/COPYING
+++ b/util/COPYING
diff --git a/util/Makefile.am b/util/Makefile.am
index ee8cf87f3..82d0a804c 100755..100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -11,9 +11,11 @@ SUBDIRS += cairo-script
endif
if CAIRO_HAS_TRACE
+SUBDIRS += cairo-trace
if CAIRO_HAS_DLSYM
if CAIRO_HAS_SCRIPT_SURFACE
if CAIRO_HAS_TEE_SURFACE
+SUBDIRS += cairo-fdr
endif
endif
endif
@@ -23,6 +25,7 @@ if BUILD_SPHINX
if CAIRO_HAS_DLSYM
if CAIRO_HAS_SCRIPT_SURFACE
if CAIRO_HAS_TEE_SURFACE
+SUBDIRS += cairo-sphinx
endif
endif
endif
diff --git a/util/README b/util/README
index 39560a8c3..39560a8c3 100755..100644
--- a/util/README
+++ b/util/README
diff --git a/util/backtrace-symbols.c b/util/backtrace-symbols.c
new file mode 100644
index 000000000..045ad7805
--- /dev/null
+++ b/util/backtrace-symbols.c
@@ -0,0 +1,377 @@
+/*
+ A hacky replacement for backtrace_symbols in glibc
+
+ backtrace_symbols in glibc looks up symbols using dladdr which is limited in
+ the symbols that it sees. libbacktracesymbols opens the executable and shared
+ libraries using libbfd and will look up backtrace information using the symbol
+ table and the dwarf line information.
+
+ It may make more sense for this program to use libelf instead of libbfd.
+ However, I have not investigated that yet.
+
+ Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
+
+ Copyright 2007 Jeff Muizelaar
+*/
+
+/* addr2line.c -- convert addresses to line number and function name
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
+
+ This file was part of GNU Binutils.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. */
+
+#define fatal(a, b) exit(1)
+#define bfd_fatal(a) exit(1)
+#define bfd_nonfatal(a) exit(1)
+#define list_matching_formats(a) exit(1)
+
+/* 2 characters for each byte, plus 1 each for 0, x, and NULL */
+#define PTRSTR_LEN (sizeof(void *) * 2 + 3)
+#define true 1
+#define false 0
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <execinfo.h>
+#include <bfd.h>
+#include <libiberty.h>
+#include <dlfcn.h>
+#include <link.h>
+#if 0
+
+void (*dbfd_init)(void);
+bfd_vma (*dbfd_scan_vma)(const char *string, const char **end, int base);
+bfd* (*dbfd_openr)(const char *filename, const char *target);
+bfd_boolean (*dbfd_check_format)(bfd *abfd, bfd_format format);
+bfd_boolean (*dbfd_check_format_matches)(bfd *abfd, bfd_format format, char ***matching);
+bfd_boolean (*dbfd_close)(bfd *abfd);
+bfd_boolean (*dbfd_map_over_sections)(bfd *abfd, void (*func)(bfd *abfd, asection *sect, void *obj),
+ void *obj);
+#define bfd_init dbfd_init
+
+static void load_funcs(void)
+{
+ void * handle = dlopen("libbfd.so", RTLD_NOW);
+ dbfd_init = dlsym(handle, "bfd_init");
+ dbfd_scan_vma = dlsym(handle, "bfd_scan_vma");
+ dbfd_openr = dlsym(handle, "bfd_openr");
+ dbfd_check_format = dlsym(handle, "bfd_check_format");
+ dbfd_check_format_matches = dlsym(handle, "bfd_check_format_matches");
+ dbfd_close = dlsym(handle, "bfd_close");
+ dbfd_map_over_sections = dlsym(handle, "bfd_map_over_sections");
+}
+
+#endif
+
+
+static asymbol **syms; /* Symbol table. */
+
+/* 150 isn't special; it's just an arbitrary non-ASCII char value. */
+#define OPTION_DEMANGLER (150)
+
+static void slurp_symtab(bfd * abfd);
+static void find_address_in_section(bfd *abfd, asection *section, void *data);
+
+/* Read in the symbol table. */
+
+static void slurp_symtab(bfd * abfd)
+{
+ long symcount;
+ unsigned int size;
+
+ if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
+ return;
+
+ symcount = bfd_read_minisymbols(abfd, false, (PTR) & syms, &size);
+ if (symcount == 0)
+ symcount = bfd_read_minisymbols(abfd, true /* dynamic */ ,
+ (PTR) & syms, &size);
+
+ if (symcount < 0)
+ bfd_fatal(bfd_get_filename(abfd));
+}
+
+/* These global variables are used to pass information between
+ translate_addresses and find_address_in_section. */
+
+static bfd_vma pc;
+static const char *filename;
+static const char *functionname;
+static unsigned int line;
+static int found;
+
+/* Look for an address in a section. This is called via
+ bfd_map_over_sections. */
+
+static void find_address_in_section(bfd *abfd, asection *section, void *data __attribute__ ((__unused__)) )
+{
+ bfd_vma vma;
+ bfd_size_type size;
+
+ if (found)
+ return;
+
+ if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
+ return;
+
+ vma = bfd_get_section_vma(abfd, section);
+ if (pc < vma)
+ return;
+
+ size = bfd_section_size(abfd, section);
+ if (pc >= vma + size)
+ return;
+
+ found = bfd_find_nearest_line(abfd, section, syms, pc - vma,
+ &filename, &functionname, &line);
+}
+
+/* Read hexadecimal addresses from stdin, translate into
+ file_name:line_number and optionally function name. */
+#if 0
+static void translate_addresses(bfd * abfd, char (*addr)[PTRSTR_LEN], int naddr)
+{
+ while (naddr) {
+ pc = bfd_scan_vma(addr[naddr-1], NULL, 16);
+
+ found = false;
+ bfd_map_over_sections(abfd, find_address_in_section,
+ (PTR) NULL);
+
+ if (!found) {
+ printf("[%s] \?\?() \?\?:0\n",addr[naddr-1]);
+ } else {
+ const char *name;
+
+ name = functionname;
+ if (name == NULL || *name == '\0')
+ name = "??";
+ if (filename != NULL) {
+ char *h;
+
+ h = strrchr(filename, '/');
+ if (h != NULL)
+ filename = h + 1;
+ }
+
+ printf("\t%s:%u\t", filename ? filename : "??",
+ line);
+
+ printf("%s()\n", name);
+
+ }
+
+ /* fflush() is essential for using this command as a server
+ child process that reads addresses from a pipe and responds
+ with line number information, processing one address at a
+ time. */
+ fflush(stdout);
+ naddr--;
+ }
+}
+#endif
+
+static char** translate_addresses_buf(bfd * abfd, bfd_vma *addr, int naddr)
+{
+ int naddr_orig = naddr;
+ char b;
+ int total = 0;
+ enum { Count, Print } state;
+ char *buf = &b;
+ int len = 0;
+ char **ret_buf = NULL;
+ /* iterate over the formating twice.
+ * the first time we count how much space we need
+ * the second time we do the actual printing */
+ for (state=Count; state<=Print; state++) {
+ if (state == Print) {
+ ret_buf = malloc(total + sizeof(char*)*naddr);
+ buf = (char*)(ret_buf + naddr);
+ len = total;
+ }
+ while (naddr) {
+ if (state == Print)
+ ret_buf[naddr-1] = buf;
+ pc = addr[naddr-1];
+
+ found = false;
+ bfd_map_over_sections(abfd, find_address_in_section,
+ (PTR) NULL);
+
+ if (!found) {
+ total += snprintf(buf, len, "[0x%llx] \?\?() \?\?:0",(long long unsigned int) addr[naddr-1]) + 1;
+ } else {
+ const char *name;
+
+ name = functionname;
+ if (name == NULL || *name == '\0')
+ name = "??";
+ if (filename != NULL) {
+ char *h;
+
+ h = strrchr(filename, '/');
+ if (h != NULL)
+ filename = h + 1;
+ }
+ total += snprintf(buf, len, "%s:%u\t%s()", filename ? filename : "??",
+ line, name) + 1;
+
+ }
+ if (state == Print) {
+ /* set buf just past the end of string */
+ buf = buf + total + 1;
+ }
+ naddr--;
+ }
+ naddr = naddr_orig;
+ }
+ return ret_buf;
+}
+/* Process a file. */
+
+static char **process_file(const char *file_name, bfd_vma *addr, int naddr)
+{
+ bfd *abfd;
+ char **matching;
+ char **ret_buf;
+
+ abfd = bfd_openr(file_name, NULL);
+
+ if (abfd == NULL)
+ bfd_fatal(file_name);
+
+ if (bfd_check_format(abfd, bfd_archive))
+ fatal("%s: can not get addresses from archive", file_name);
+
+ if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
+ bfd_nonfatal(bfd_get_filename(abfd));
+ if (bfd_get_error() ==
+ bfd_error_file_ambiguously_recognized) {
+ list_matching_formats(matching);
+ free(matching);
+ }
+ xexit(1);
+ }
+
+ slurp_symtab(abfd);
+
+ ret_buf = translate_addresses_buf(abfd, addr, naddr);
+
+ free (syms);
+ syms = NULL;
+
+ bfd_close(abfd);
+ return ret_buf;
+}
+
+#define MAX_DEPTH 16
+
+struct file_match {
+ const char *file;
+ void *address;
+ void *base;
+ void *hdr;
+};
+
+static int find_matching_file(struct dl_phdr_info *info,
+ size_t size, void *data)
+{
+ struct file_match *match = data;
+ /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
+ long n;
+ const ElfW(Phdr) *phdr;
+ ElfW(Addr) load_base = info->dlpi_addr;
+ phdr = info->dlpi_phdr;
+ for (n = info->dlpi_phnum; --n >= 0; phdr++) {
+ if (phdr->p_type == PT_LOAD) {
+ ElfW(Addr) vaddr = phdr->p_vaddr + load_base;
+ if (match->address >= vaddr && match->address < vaddr + phdr->p_memsz) {
+ /* we found a match */
+ match->file = info->dlpi_name;
+ match->base = info->dlpi_addr;
+ }
+ }
+ }
+ return 0;
+}
+
+char **backtrace_symbols(void *const *buffer, int size)
+{
+ int stack_depth = size - 1;
+ int x,y;
+ /* discard calling function */
+ int total = 0;
+
+ char ***locations;
+ char **final;
+ char *f_strings;
+
+ locations = malloc(sizeof(char**) * (stack_depth+1));
+
+ bfd_init();
+ for(x=stack_depth, y=0; x>=0; x--, y++){
+ struct file_match match = { .address = buffer[x] };
+ char **ret_buf;
+ bfd_vma addr;
+ dl_iterate_phdr(find_matching_file, &match);
+ addr = buffer[x] - match.base;
+ if (match.file && strlen(match.file))
+ ret_buf = process_file(match.file, &addr, 1);
+ else
+ ret_buf = process_file("/proc/self/exe", &addr, 1);
+ locations[x] = ret_buf;
+ total += strlen(ret_buf[0]) + 1;
+ }
+
+ /* allocate the array of char* we are going to return and extra space for
+ * all of the strings */
+ final = malloc(total + (stack_depth + 1) * sizeof(char*));
+ /* get a pointer to the extra space */
+ f_strings = (char*)(final + stack_depth + 1);
+
+ /* fill in all of strings and pointers */
+ for(x=stack_depth; x>=0; x--){
+ strcpy(f_strings, locations[x][0]);
+ free(locations[x]);
+ final[x] = f_strings;
+ f_strings += strlen(f_strings) + 1;
+ }
+
+ free(locations);
+
+ return final;
+}
+
+void
+backtrace_symbols_fd(void *const *buffer, int size, int fd)
+{
+ int j;
+ char **strings;
+
+ strings = backtrace_symbols(buffer, size);
+ if (strings == NULL) {
+ perror("backtrace_symbols");
+ exit(EXIT_FAILURE);
+ }
+
+ for (j = 0; j < size; j++)
+ printf("%s\n", strings[j]);
+
+ free(strings);
+}
diff --git a/util/cairo-fdr/Makefile.am b/util/cairo-fdr/Makefile.am
new file mode 100644
index 000000000..34215a659
--- /dev/null
+++ b/util/cairo-fdr/Makefile.am
@@ -0,0 +1,15 @@
+cairolibdir = $(libdir)/cairo
+
+#bin_SCRIPTS = cairo-fdr
+cairolib_LTLIBRARIES = cairo-fdr.la
+
+AM_CPPFLAGS = -I$(top_srcdir)/src \
+ -I$(top_builddir)/src
+
+cairo_fdr_la_SOURCES = fdr.c
+cairo_fdr_la_CPPFLAGS = $(AM_CPPFLAGS)
+cairo_fdr_la_CFLAGS = $(CAIRO_CFLAGS)
+cairo_fdr_la_LDFLAGS = -module -no-undefined
+if CAIRO_HAS_DL
+cairo_fdr_la_LIBADD = -ldl
+endif
diff --git a/util/cairo-fdr/fdr.c b/util/cairo-fdr/fdr.c
new file mode 100644
index 000000000..08d9c0113
--- /dev/null
+++ b/util/cairo-fdr/fdr.c
@@ -0,0 +1,331 @@
+/* cairo-fdr - a 'flight data recorder', a black box, for cairo
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cairo.h>
+#include <cairo-script.h>
+#include <cairo-tee.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <signal.h>
+
+#include <dlfcn.h>
+
+static void *_dlhandle = RTLD_NEXT;
+#define DLCALL(name, args...) ({ \
+ static typeof (&name) name##_real; \
+ if (name##_real == NULL) { \
+ name##_real = dlsym (_dlhandle, #name); \
+ if (name##_real == NULL && _dlhandle == RTLD_NEXT) { \
+ _dlhandle = dlopen ("libcairo.so", RTLD_LAZY); \
+ name##_real = dlsym (_dlhandle, #name); \
+ assert (name##_real != NULL); \
+ } \
+ } \
+ (*name##_real) (args); \
+})
+
+#define RINGBUFFER_SIZE 16
+static cairo_surface_t *fdr_ringbuffer[RINGBUFFER_SIZE];
+static int fdr_position;
+static int fdr_dump;
+
+static const cairo_user_data_key_t fdr_key;
+
+static void
+fdr_replay_to_script (cairo_surface_t *recording, cairo_device_t *ctx)
+{
+ if (recording != NULL) {
+ DLCALL (cairo_script_write_comment, ctx, "--- fdr ---", -1);
+ DLCALL (cairo_script_from_recording_surface, ctx, recording);
+ }
+}
+
+static void
+fdr_dump_ringbuffer (void)
+{
+ cairo_device_t *ctx;
+ int n;
+
+ ctx = DLCALL (cairo_script_create, "/tmp/fdr.trace");
+
+ for (n = fdr_position; n < RINGBUFFER_SIZE; n++)
+ fdr_replay_to_script (fdr_ringbuffer[n], ctx);
+
+ for (n = 0; n < fdr_position; n++)
+ fdr_replay_to_script (fdr_ringbuffer[n], ctx);
+
+ DLCALL (cairo_device_destroy, ctx);
+}
+
+static void
+fdr_sighandler (int sig)
+{
+ fdr_dump = 1;
+}
+
+static void
+fdr_urgent_sighandler (int sig)
+{
+ fdr_dump_ringbuffer ();
+}
+
+static void
+fdr_atexit (void)
+{
+ if (fdr_dump)
+ fdr_dump_ringbuffer ();
+}
+
+static void
+fdr_pending_signals (void)
+{
+ static int initialized;
+
+ if (! initialized) {
+ initialized = 1;
+
+ signal (SIGUSR1, fdr_sighandler);
+
+ signal (SIGSEGV, fdr_urgent_sighandler);
+ signal (SIGABRT, fdr_urgent_sighandler);
+ atexit (fdr_atexit);
+ }
+
+ if (fdr_dump) {
+ fdr_dump_ringbuffer ();
+ fdr_dump = 0;
+ }
+}
+
+static void
+fdr_get_extents (cairo_surface_t *surface,
+ cairo_rectangle_t *extents)
+{
+ cairo_t *cr;
+
+ cr = DLCALL (cairo_create, surface);
+ DLCALL (cairo_clip_extents, cr,
+ &extents->x, &extents->y, &extents->width, &extents->height);
+ DLCALL (cairo_destroy, cr);
+
+ extents->width -= extents->x;
+ extents->height -= extents->y;
+}
+
+static void
+fdr_surface_destroy (void *surface)
+{
+ DLCALL (cairo_surface_destroy, surface);
+}
+
+static void
+fdr_surface_reference (void *surface)
+{
+ DLCALL (cairo_surface_reference, surface);
+}
+
+static cairo_surface_t *
+fdr_surface_get_tee (cairo_surface_t *surface)
+{
+ return DLCALL (cairo_surface_get_user_data, surface, &fdr_key);
+}
+
+static cairo_surface_t *
+fdr_tee_surface_index (cairo_surface_t *surface, int index)
+{
+ return DLCALL (cairo_tee_surface_index, surface, index);
+}
+
+cairo_t *
+cairo_create (cairo_surface_t *surface)
+{
+ cairo_surface_t *record, *tee;
+
+ fdr_pending_signals ();
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee == NULL) {
+ cairo_rectangle_t extents;
+ cairo_content_t content;
+
+ fdr_get_extents (surface, &extents);
+ content = DLCALL (cairo_surface_get_content, surface);
+
+ tee = DLCALL (cairo_tee_surface_create, surface);
+ record = DLCALL (cairo_recording_surface_create, content, &extents);
+ DLCALL (cairo_tee_surface_add, tee, record);
+
+ DLCALL (cairo_surface_set_user_data, surface,
+ &fdr_key, tee, fdr_surface_destroy);
+ } else {
+ int n;
+
+ record = fdr_tee_surface_index (tee, 1);
+
+ /* update the position of the recording surface in the ringbuffer */
+ for (n = 0; n < RINGBUFFER_SIZE; n++) {
+ if (record == fdr_ringbuffer[n]) {
+ fdr_ringbuffer[n] = NULL;
+ break;
+ }
+ }
+ }
+
+ fdr_surface_destroy (fdr_ringbuffer[fdr_position]);
+ fdr_ringbuffer[fdr_position] = record;
+ fdr_position = (fdr_position + 1) % RINGBUFFER_SIZE;
+
+ return DLCALL (cairo_create, tee);
+}
+
+static void
+fdr_remove_tee (cairo_surface_t *surface)
+{
+ fdr_surface_reference (surface);
+ DLCALL (cairo_surface_set_user_data, surface, &fdr_key, NULL, NULL);
+ fdr_surface_destroy (surface);
+}
+
+void
+cairo_destroy (cairo_t *cr)
+{
+ cairo_surface_t *tee;
+
+ tee = DLCALL (cairo_get_target, cr);
+ DLCALL (cairo_destroy, cr);
+
+ if (DLCALL (cairo_surface_get_reference_count, tee) == 1)
+ fdr_remove_tee (fdr_tee_surface_index (tee, 0));
+}
+
+void
+cairo_pattern_destroy (cairo_pattern_t *pattern)
+{
+ if (DLCALL (cairo_pattern_get_type, pattern) == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_t *surface;
+
+ if (DLCALL (cairo_pattern_get_surface, pattern, &surface) == CAIRO_STATUS_SUCCESS &&
+ DLCALL (cairo_surface_get_type, surface) == CAIRO_SURFACE_TYPE_TEE &&
+ DLCALL (cairo_surface_get_reference_count, surface) == 2)
+ {
+ fdr_remove_tee (fdr_tee_surface_index (surface, 0));
+ }
+ }
+
+ DLCALL (cairo_pattern_destroy, pattern);
+}
+
+cairo_surface_t *
+cairo_get_target (cairo_t *cr)
+{
+ cairo_surface_t *tee;
+
+ tee = DLCALL (cairo_get_target, cr);
+ return fdr_tee_surface_index (tee, 0);
+}
+
+cairo_surface_t *
+cairo_get_group_target (cairo_t *cr)
+{
+ cairo_surface_t *tee;
+
+ tee = DLCALL (cairo_get_group_target, cr);
+ return fdr_tee_surface_index (tee, 0);
+}
+
+cairo_pattern_t *
+cairo_pattern_create_for_surface (cairo_surface_t *surface)
+{
+ cairo_surface_t *tee;
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee != NULL)
+ surface = tee;
+
+ return DLCALL (cairo_pattern_create_for_surface, surface);
+}
+
+cairo_status_t
+cairo_pattern_get_surface (cairo_pattern_t *pattern,
+ cairo_surface_t **surface)
+{
+ cairo_status_t status;
+ cairo_surface_t *tee;
+
+ status = DLCALL (cairo_pattern_get_surface, pattern, surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ return status;
+
+ tee = fdr_surface_get_tee (*surface);
+ if (tee != NULL)
+ *surface = tee;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+cairo_set_source_surface (cairo_t *cr,
+ cairo_surface_t *surface,
+ double x, double y)
+{
+ cairo_surface_t *tee;
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee != NULL)
+ surface = tee;
+
+ DLCALL (cairo_set_source_surface, cr, surface, x, y);
+}
+
+cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t *surface,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_surface_t *tee;
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee != NULL)
+ surface = tee;
+
+ return DLCALL (cairo_surface_create_similar,
+ surface, content, width, height);
+}
+
+cairo_surface_t *
+cairo_surface_create_for_rectangle (cairo_surface_t *surface,
+ double x,
+ double y,
+ double width,
+ double height)
+{
+ cairo_surface_t *tee;
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee != NULL)
+ surface = tee;
+
+ return DLCALL (cairo_surface_create_for_rectangle,
+ surface, x, y, width, height);
+}
diff --git a/util/cairo-gobject/Makefile.am b/util/cairo-gobject/Makefile.am
index 22c1a278a..22c1a278a 100755..100644
--- a/util/cairo-gobject/Makefile.am
+++ b/util/cairo-gobject/Makefile.am
diff --git a/util/cairo-gobject/cairo-gobject-enums.c b/util/cairo-gobject/cairo-gobject-enums.c
index 0a7c95d29..0a7c95d29 100755..100644
--- a/util/cairo-gobject/cairo-gobject-enums.c
+++ b/util/cairo-gobject/cairo-gobject-enums.c
diff --git a/util/cairo-gobject/cairo-gobject-structs.c b/util/cairo-gobject/cairo-gobject-structs.c
index 05e3ece80..4bbf11baa 100755..100644
--- a/util/cairo-gobject/cairo-gobject-structs.c
+++ b/util/cairo-gobject/cairo-gobject-structs.c
@@ -78,10 +78,12 @@ cairo_gobject_cairo_ ## name ## _copy (gpointer src) \
return g_memdup (src, sizeof (cairo_ ## name ## _t)); \
}
+COPY_FUNC (matrix)
+CAIRO_DEFINE_BOXED ("CairoMatrix", cairo_gobject_matrix,
+ cairo_gobject_cairo_matrix_copy, g_free);
COPY_FUNC (rectangle)
CAIRO_DEFINE_BOXED ("CairoRectangle", cairo_gobject_rectangle,
cairo_gobject_cairo_rectangle_copy, g_free);
COPY_FUNC (rectangle_int)
CAIRO_DEFINE_BOXED ("CairoRectangleInt", cairo_gobject_rectangle_int,
cairo_gobject_cairo_rectangle_int_copy, g_free);
-
diff --git a/util/cairo-gobject/cairo-gobject.h b/util/cairo-gobject/cairo-gobject.h
index e82cbc0d1..459074e83 100755..100644
--- a/util/cairo-gobject/cairo-gobject.h
+++ b/util/cairo-gobject/cairo-gobject.h
@@ -55,6 +55,10 @@ cairo_gobject_context_get_type (void);
cairo_public GType
cairo_gobject_device_get_type (void);
+#define CAIRO_GOBJECT_TYPE_MATRIX cairo_gobject_matrix_get_type ()
+cairo_public GType
+cairo_gobject_matrix_get_type (void);
+
#define CAIRO_GOBJECT_TYPE_PATTERN cairo_gobject_pattern_get_type ()
cairo_public GType
cairo_gobject_pattern_get_type (void);
diff --git a/util/cairo-missing/Makefile.am b/util/cairo-missing/Makefile.am
index 907861026..907861026 100755..100644
--- a/util/cairo-missing/Makefile.am
+++ b/util/cairo-missing/Makefile.am
diff --git a/util/cairo-missing/Makefile.sources b/util/cairo-missing/Makefile.sources
index 1a306314a..1a306314a 100755..100644
--- a/util/cairo-missing/Makefile.sources
+++ b/util/cairo-missing/Makefile.sources
diff --git a/util/cairo-missing/Makefile.win32 b/util/cairo-missing/Makefile.win32
index c2c5bc01e..c2c5bc01e 100755..100644
--- a/util/cairo-missing/Makefile.win32
+++ b/util/cairo-missing/Makefile.win32
diff --git a/util/cairo-missing/cairo-missing.h b/util/cairo-missing/cairo-missing.h
index 7e4f0a37d..741b498a8 100755..100644
--- a/util/cairo-missing/cairo-missing.h
+++ b/util/cairo-missing/cairo-missing.h
@@ -41,7 +41,7 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
-#ifndef _SSIZE_T_DEFINED
+#if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_)
typedef SSIZE_T ssize_t;
#endif
#endif
diff --git a/util/cairo-missing/getline.c b/util/cairo-missing/getline.c
index 8585cfdc2..8585cfdc2 100755..100644
--- a/util/cairo-missing/getline.c
+++ b/util/cairo-missing/getline.c
diff --git a/util/cairo-missing/strndup.c b/util/cairo-missing/strndup.c
index 280ea3017..280ea3017 100755..100644
--- a/util/cairo-missing/strndup.c
+++ b/util/cairo-missing/strndup.c
diff --git a/util/cairo-script/.gitignore b/util/cairo-script/.gitignore
index 8ecaee3e5..8ecaee3e5 100755..100644
--- a/util/cairo-script/.gitignore
+++ b/util/cairo-script/.gitignore
diff --git a/util/cairo-script/COPYING b/util/cairo-script/COPYING
index 66ad7840f..66ad7840f 100755..100644
--- a/util/cairo-script/COPYING
+++ b/util/cairo-script/COPYING
diff --git a/util/cairo-script/Makefile.am b/util/cairo-script/Makefile.am
index d5c2998ac..d5c2998ac 100755..100644
--- a/util/cairo-script/Makefile.am
+++ b/util/cairo-script/Makefile.am
diff --git a/util/cairo-script/Makefile.sources b/util/cairo-script/Makefile.sources
index fd73a17cf..fd73a17cf 100755..100644
--- a/util/cairo-script/Makefile.sources
+++ b/util/cairo-script/Makefile.sources
diff --git a/util/cairo-script/Makefile.win32 b/util/cairo-script/Makefile.win32
index 0aef981c1..0aef981c1 100755..100644
--- a/util/cairo-script/Makefile.win32
+++ b/util/cairo-script/Makefile.win32
diff --git a/util/cairo-script/cairo-script-file.c b/util/cairo-script/cairo-script-file.c
index 85d292c47..0274a3e7c 100755..100644
--- a/util/cairo-script/cairo-script-file.c
+++ b/util/cairo-script/cairo-script-file.c
@@ -176,21 +176,21 @@ csi_file_new_from_string (csi_t *ctx,
status = _csi_error (CAIRO_STATUS_NO_MEMORY);
break;
-#if HAVE_ZLIB
case ZLIB:
+#if HAVE_ZLIB
if (uncompress ((Bytef *) tmp_str->string, &len,
(Bytef *) src->string, src->len) != Z_OK)
+#endif
status = _csi_error (CAIRO_STATUS_NO_MEMORY);
break;
-#endif
-#if HAVE_LZO
case LZO:
+#if HAVE_LZO
if (lzo2a_decompress ((lzo_bytep) src->string, src->len,
(lzo_bytep) tmp_str->string, &len,
NULL))
+#endif
status = _csi_error (CAIRO_STATUS_NO_MEMORY);
break;
-#endif
}
if (_csi_unlikely (status)) {
csi_string_free (ctx, tmp_str);
@@ -1063,7 +1063,6 @@ _csi_file_as_string (csi_t *ctx,
unsigned int allocated;
csi_status_t status;
- len = 0;
allocated = 16384;
bytes = _csi_alloc (ctx, allocated);
if (bytes == NULL)
diff --git a/util/cairo-script/cairo-script-hash.c b/util/cairo-script/cairo-script-hash.c
index 0a230e8d7..0a230e8d7 100755..100644
--- a/util/cairo-script/cairo-script-hash.c
+++ b/util/cairo-script/cairo-script-hash.c
diff --git a/util/cairo-script/cairo-script-interpreter.c b/util/cairo-script/cairo-script-interpreter.c
index bdd525542..50170fc29 100755..100644
--- a/util/cairo-script/cairo-script-interpreter.c
+++ b/util/cairo-script/cairo-script-interpreter.c
@@ -634,7 +634,7 @@ cairo_script_interpreter_finish (csi_t *ctx)
if (! ctx->finished) {
_csi_finish (ctx);
ctx->finished = 1;
- } else if (status == CAIRO_STATUS_SUCCESS) {
+ } else if (status == CSI_STATUS_SUCCESS) {
status = ctx->status = CSI_STATUS_INTERPRETER_FINISHED;
}
diff --git a/util/cairo-script/cairo-script-interpreter.h b/util/cairo-script/cairo-script-interpreter.h
index 27fb98661..27fb98661 100755..100644
--- a/util/cairo-script/cairo-script-interpreter.h
+++ b/util/cairo-script/cairo-script-interpreter.h
diff --git a/util/cairo-script/cairo-script-objects.c b/util/cairo-script/cairo-script-objects.c
index a625489b1..a625489b1 100755..100644
--- a/util/cairo-script/cairo-script-objects.c
+++ b/util/cairo-script/cairo-script-objects.c
diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index b14579c4a..d7cd203f5 100755..100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -1769,28 +1769,28 @@ inflate_string (csi_t *ctx, csi_string_t *src)
free (bytes);
return NULL;
-#if HAVE_ZLIB
case ZLIB:
+#if HAVE_ZLIB
if (uncompress ((Bytef *) bytes, &len,
(Bytef *) src->string, src->len) != Z_OK)
+#endif
{
_csi_free (ctx, bytes);
return NULL;
}
break;
-#endif
-#if HAVE_LZO
case LZO:
+#if HAVE_LZO
if (lzo2a_decompress ((Bytef *) src->string, src->len,
(Bytef *) bytes, &len,
NULL))
+#endif
{
_csi_free (ctx, bytes);
return NULL;
}
break;
-#endif
}
bytes[len] = '\0';
@@ -1949,6 +1949,18 @@ _ft_create_for_pattern (csi_t *ctx,
}
pattern = FcNameParse (bytes);
+ if (!pattern)
+ {
+ /* Fontconfig's representation of charset changed mid 2014;
+ * We used to record charset before that. Remove everything
+ * after charset if that's present, and try again. */
+ char *s = strstr ((char *) bytes, ":charset=");
+ if (s)
+ {
+ *s = '\0';
+ pattern = FcNameParse (bytes);
+ }
+ }
if (bytes != tmpl.bytes)
_csi_free (ctx, bytes);
@@ -2983,22 +2995,22 @@ err_decompress:
cairo_surface_destroy (image);
return _csi_error (CSI_STATUS_READ_ERROR);
-#if HAVE_ZLIB
case ZLIB:
+#if HAVE_ZLIB
if (uncompress ((Bytef *) data, &out,
(Bytef *) s->string, s->len) != Z_OK)
+#endif
goto err_decompress;
break;
-#endif
-#if HAVE_LZO
case LZO:
+#if HAVE_LZO
if (lzo2a_decompress ((Bytef *) s->string, s->len,
(Bytef *) data, &out,
NULL))
+#endif
goto err_decompress;
break;
-#endif
}
}
else
@@ -4866,6 +4878,30 @@ _set_device_offset (csi_t *ctx)
}
static csi_status_t
+_set_device_scale (csi_t *ctx)
+{
+ csi_status_t status;
+ cairo_surface_t *surface;
+ double x, y;
+
+ check (3);
+
+ status = _csi_ostack_get_number (ctx, 0, &y);
+ if (_csi_unlikely (status))
+ return status;
+ status = _csi_ostack_get_number (ctx, 1, &x);
+ if (_csi_unlikely (status))
+ return status;
+ status = _csi_ostack_get_surface (ctx, 2, &surface);
+ if (_csi_unlikely (status))
+ return status;
+
+ cairo_surface_set_device_scale (surface, x, y);
+ pop (2);
+ return CSI_STATUS_SUCCESS;
+}
+
+static csi_status_t
_set_extend (csi_t *ctx)
{
csi_status_t status;
@@ -6146,6 +6182,29 @@ _surface (csi_t *ctx)
}
}
+ status = csi_name_new_static (ctx, &key, "device-scale");
+ if (_csi_unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return status;
+ }
+ if (csi_dictionary_has (dict, key.datum.name)) {
+ status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
+ if (_csi_unlikely (status))
+ return status;
+
+ if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_ARRAY) {
+ csi_array_t *array = obj.datum.array;
+
+ if (array->stack.len == 2) {
+ cairo_surface_set_device_scale (surface,
+ csi_number_get_value
+ (&array->stack.objects[0]),
+ csi_number_get_value
+ (&array->stack.objects[1]));
+ }
+ }
+ }
+
obj.type = CSI_OBJECT_TYPE_SURFACE;
obj.datum.surface = surface;
pop (1);
@@ -6582,6 +6641,7 @@ _defs[] = {
{ "set-antialias", _set_antialias },
{ "set-dash", _set_dash },
{ "set-device-offset", _set_device_offset },
+ { "set-device-scale", _set_device_scale },
{ "set-extend", _set_extend },
{ "set-fallback-resolution", _set_fallback_resolution },
{ "set-fill-rule", _set_fill_rule },
diff --git a/util/cairo-script/cairo-script-private.h b/util/cairo-script/cairo-script-private.h
index 6bf41b4e5..6bf41b4e5 100755..100644
--- a/util/cairo-script/cairo-script-private.h
+++ b/util/cairo-script/cairo-script-private.h
diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
index b9d445bb3..b9d445bb3 100755..100644
--- a/util/cairo-script/cairo-script-scanner.c
+++ b/util/cairo-script/cairo-script-scanner.c
diff --git a/util/cairo-script/cairo-script-stack.c b/util/cairo-script/cairo-script-stack.c
index b1d146c52..b1d146c52 100755..100644
--- a/util/cairo-script/cairo-script-stack.c
+++ b/util/cairo-script/cairo-script-stack.c
diff --git a/util/cairo-script/csi-bind.c b/util/cairo-script/csi-bind.c
index 91b58fb95..91b58fb95 100755..100644
--- a/util/cairo-script/csi-bind.c
+++ b/util/cairo-script/csi-bind.c
diff --git a/util/cairo-script/csi-exec.c b/util/cairo-script/csi-exec.c
index d30b1c9c8..d30b1c9c8 100755..100644
--- a/util/cairo-script/csi-exec.c
+++ b/util/cairo-script/csi-exec.c
diff --git a/util/cairo-script/csi-replay.c b/util/cairo-script/csi-replay.c
index 67fed3b33..67fed3b33 100755..100644
--- a/util/cairo-script/csi-replay.c
+++ b/util/cairo-script/csi-replay.c
diff --git a/util/cairo-script/csi-trace.c b/util/cairo-script/csi-trace.c
index c57a56b18..a0466a35b 100755..100644
--- a/util/cairo-script/csi-trace.c
+++ b/util/cairo-script/csi-trace.c
@@ -2,6 +2,7 @@
#include <cairo-script-interpreter.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <libgen.h>
@@ -22,11 +23,18 @@ main (int argc, char **argv)
.surface_create = _script_surface_create,
};
int i;
+ char buf[4096];
csi = cairo_script_interpreter_create ();
for (i = 1; i < argc; i++) {
- char buf[4096];
+ if (strcmp (argv[i], "--version")) {
+ printf ("%s: version %s\n", argv[0], __DATE__);
+ exit (0);
+ } else if (strcmp (argv[i], "--help")) {
+ printf ("usage: %s < in > out\n", argv[0]);
+ exit (0);
+ }
snprintf (buf, sizeof (buf), "%s.trace", basename (argv[i]));
cairo_device_destroy (hooks.closure);
diff --git a/util/cairo-script/examples/Makefile.am b/util/cairo-script/examples/Makefile.am
index a87f02d9a..a87f02d9a 100755..100644
--- a/util/cairo-script/examples/Makefile.am
+++ b/util/cairo-script/examples/Makefile.am
diff --git a/util/cairo-script/examples/dragon.cs b/util/cairo-script/examples/dragon.cs
index 1060ca699..1060ca699 100755..100644
--- a/util/cairo-script/examples/dragon.cs
+++ b/util/cairo-script/examples/dragon.cs
diff --git a/util/cairo-script/examples/hilbert.cs b/util/cairo-script/examples/hilbert.cs
index 4278bf74c..4278bf74c 100755..100644
--- a/util/cairo-script/examples/hilbert.cs
+++ b/util/cairo-script/examples/hilbert.cs
diff --git a/util/cairo-script/examples/infinichess.cs b/util/cairo-script/examples/infinichess.cs
index f82b10278..f82b10278 100755..100644
--- a/util/cairo-script/examples/infinichess.cs
+++ b/util/cairo-script/examples/infinichess.cs
diff --git a/util/cairo-script/examples/interference.cs b/util/cairo-script/examples/interference.cs
index 6d2ee2200..6d2ee2200 100755..100644
--- a/util/cairo-script/examples/interference.cs
+++ b/util/cairo-script/examples/interference.cs
diff --git a/util/cairo-script/examples/pythagoras-tree.cs b/util/cairo-script/examples/pythagoras-tree.cs
index 96b4b39bc..96b4b39bc 100755..100644
--- a/util/cairo-script/examples/pythagoras-tree.cs
+++ b/util/cairo-script/examples/pythagoras-tree.cs
diff --git a/util/cairo-script/examples/sierpinski.cs b/util/cairo-script/examples/sierpinski.cs
index 6f959083b..6f959083b 100755..100644
--- a/util/cairo-script/examples/sierpinski.cs
+++ b/util/cairo-script/examples/sierpinski.cs
diff --git a/util/cairo-script/examples/wedgeAnnulus_crop_ybRings.cs b/util/cairo-script/examples/wedgeAnnulus_crop_ybRings.cs
index 5aeb97f6e..5aeb97f6e 100755..100644
--- a/util/cairo-script/examples/wedgeAnnulus_crop_ybRings.cs
+++ b/util/cairo-script/examples/wedgeAnnulus_crop_ybRings.cs
diff --git a/util/cairo-script/examples/world-map.cs b/util/cairo-script/examples/world-map.cs
index 54d9a8f31..54d9a8f31 100755..100644
--- a/util/cairo-script/examples/world-map.cs
+++ b/util/cairo-script/examples/world-map.cs
diff --git a/util/cairo-script/examples/zrusin.cs b/util/cairo-script/examples/zrusin.cs
index 8efc24bbe..8efc24bbe 100755..100644
--- a/util/cairo-script/examples/zrusin.cs
+++ b/util/cairo-script/examples/zrusin.cs
diff --git a/util/cairo-sphinx/.gitignore b/util/cairo-sphinx/.gitignore
new file mode 100644
index 000000000..56ecd5de8
--- /dev/null
+++ b/util/cairo-sphinx/.gitignore
@@ -0,0 +1 @@
+cairo-sphinx
diff --git a/util/cairo-sphinx/Makefile.am b/util/cairo-sphinx/Makefile.am
new file mode 100644
index 000000000..10bc10c27
--- /dev/null
+++ b/util/cairo-sphinx/Makefile.am
@@ -0,0 +1,43 @@
+cairolibdir = $(libdir)/cairo
+
+cairolib_LTLIBRARIES = cairo-sphinx.la
+bin_PROGRAMS = cairo-sphinx
+
+AM_CPPFLAGS = -I$(top_srcdir)/src \
+ -I$(top_builddir)/src \
+ -I$(top_srcdir)/boilerplate \
+ -I$(top_srcdir)/util/cairo-script
+
+cairo_sphinx_la_SOURCES = fdr.c
+cairo_sphinx_la_CPPFLAGS = $(AM_CPPFLAGS)
+cairo_sphinx_la_CFLAGS = $(CAIRO_CFLAGS)
+cairo_sphinx_la_LDFLAGS = -module -no-undefined
+if CAIRO_HAS_DL
+cairo_sphinx_la_LIBADD = -ldl
+endif
+
+cairo_sphinx_SOURCES = sphinx.c
+cairo_sphinx_CPPFLAGS = $(AM_CPPFLAGS) -DLIBDIR="\"$(cairolibdir)\""
+cairo_sphinx_CFLAGS = $(CAIRO_CFLAGS) $(real_pthread_CFLAGS) $(glib_CFLAGS)
+cairo_sphinx_LDADD = \
+ $(real_pthread_LIBS) \
+ $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+ $(top_builddir)/boilerplate/libcairoboilerplate.la \
+ $(top_builddir)/src/libcairo.la \
+ $(glib_LIBS) \
+ $(CAIRO_LDADD) \
+ $(shm_LIBS)
+cairo_sphinx_DEPENDENCIES = \
+ $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+ $(top_builddir)/boilerplate/libcairoboilerplate.la \
+ $(top_builddir)/src/libcairo.la
+
+# Install rules to rebuild the libraries and add explicit dependencies
+$(top_builddir)/boilerplate/libcairoboilerplate.la: $(top_builddir)/src/libcairo.la
+ cd $(top_builddir)/boilerplate && $(MAKE) $(AM_MAKEFLAGS) libcairoboilerplate.la
+
+$(top_builddir)/src/libcairo.la:
+ cd $(top_builddir)/src && $(MAKE) $(AM_MAKEFLAGS) libcairo.la
+
+$(top_builddir)/util/cairo-script/libcairo-script-interpreter.la: $(top_builddir)/src/libcairo.la
+ cd $(top_builddir)/util/cairo-script && $(MAKE) $(AM_MAKEFLAGS) libcairo-script-interpreter.la
diff --git a/util/cairo-sphinx/fdr.c b/util/cairo-sphinx/fdr.c
new file mode 100644
index 000000000..aeda89bcd
--- /dev/null
+++ b/util/cairo-sphinx/fdr.c
@@ -0,0 +1,261 @@
+/* cairo-fdr - a 'flight data recorder', a black box, for cairo
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cairo.h>
+#include <cairo-script.h>
+#include <cairo-tee.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <dlfcn.h>
+
+static void *_dlhandle = RTLD_NEXT;
+#define DLCALL(name, args...) ({ \
+ static typeof (&name) name##_real; \
+ if (name##_real == NULL) { \
+ name##_real = dlsym (_dlhandle, #name); \
+ if (name##_real == NULL && _dlhandle == RTLD_NEXT) { \
+ _dlhandle = dlopen ("libcairo.so", RTLD_LAZY); \
+ name##_real = dlsym (_dlhandle, #name); \
+ assert (name##_real != NULL); \
+ } \
+ } \
+ (*name##_real) (args); \
+})
+
+static cairo_device_t *fdr_context;
+static const cairo_user_data_key_t fdr_key;
+
+static void
+fdr_get_extents (cairo_surface_t *surface,
+ cairo_rectangle_t *extents)
+{
+ cairo_t *cr;
+
+ cr = DLCALL (cairo_create, surface);
+ DLCALL (cairo_clip_extents, cr,
+ &extents->x, &extents->y, &extents->width, &extents->height);
+ DLCALL (cairo_destroy, cr);
+
+ extents->width -= extents->x;
+ extents->height -= extents->y;
+}
+
+static void
+fdr_surface_destroy (void *surface)
+{
+ DLCALL (cairo_surface_destroy, surface);
+}
+
+static void
+fdr_surface_reference (void *surface)
+{
+ DLCALL (cairo_surface_reference, surface);
+}
+
+static cairo_surface_t *
+fdr_surface_get_tee (cairo_surface_t *surface)
+{
+ return DLCALL (cairo_surface_get_user_data, surface, &fdr_key);
+}
+
+static cairo_surface_t *
+fdr_tee_surface_index (cairo_surface_t *surface, int index)
+{
+ return DLCALL (cairo_tee_surface_index, surface, index);
+}
+
+static cairo_status_t
+fdr_write (void *closure, const unsigned char *data, unsigned int len)
+{
+ int fd = (int) (intptr_t) closure;
+ while (len) {
+ int ret = write (fd, data, len);
+ if (ret < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ continue;
+ default:
+ return CAIRO_STATUS_WRITE_ERROR;
+ }
+ } else if (ret == 0) {
+ return CAIRO_STATUS_WRITE_ERROR;
+ } else {
+ data += ret;
+ len -= ret;
+ }
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_t *
+cairo_create (cairo_surface_t *surface)
+{
+ cairo_surface_t *tee;
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee == NULL) {
+ cairo_surface_t *script;
+ cairo_rectangle_t extents;
+ cairo_content_t content;
+
+ if (fdr_context == NULL) {
+ const char *env = getenv ("CAIRO_SPHINX_FD");
+ int fd = env ? atoi (env) : 1;
+ fdr_context = DLCALL (cairo_script_create_for_stream,
+ fdr_write, (void *) (intptr_t) fd);
+ }
+
+ fdr_get_extents (surface, &extents);
+ content = DLCALL (cairo_surface_get_content, surface);
+
+ tee = DLCALL (cairo_tee_surface_create, surface);
+ script = DLCALL (cairo_script_surface_create,
+ fdr_context, content, extents.width, extents.height);
+ DLCALL (cairo_tee_surface_add, tee, script);
+
+ DLCALL (cairo_surface_set_user_data, surface,
+ &fdr_key, tee, fdr_surface_destroy);
+ }
+
+ return DLCALL (cairo_create, tee);
+}
+
+static void
+fdr_remove_tee (cairo_surface_t *surface)
+{
+ fdr_surface_reference (surface);
+ DLCALL (cairo_surface_set_user_data, surface, &fdr_key, NULL, NULL);
+ fdr_surface_destroy (surface);
+}
+
+void
+cairo_destroy (cairo_t *cr)
+{
+ cairo_surface_t *tee;
+
+ tee = DLCALL (cairo_get_target, cr);
+ DLCALL (cairo_destroy, cr);
+
+ if (DLCALL (cairo_surface_get_reference_count, tee) == 1)
+ fdr_remove_tee (fdr_tee_surface_index (tee, 0));
+}
+
+void
+cairo_pattern_destroy (cairo_pattern_t *pattern)
+{
+ if (DLCALL (cairo_pattern_get_type, pattern) == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_t *surface;
+
+ if (DLCALL (cairo_pattern_get_surface, pattern, &surface) == CAIRO_STATUS_SUCCESS &&
+ DLCALL (cairo_surface_get_type, surface) == CAIRO_SURFACE_TYPE_TEE &&
+ DLCALL (cairo_surface_get_reference_count, surface) == 2)
+ {
+ fdr_remove_tee (fdr_tee_surface_index (surface, 0));
+ }
+ }
+
+ DLCALL (cairo_pattern_destroy, pattern);
+}
+
+cairo_surface_t *
+cairo_get_target (cairo_t *cr)
+{
+ cairo_surface_t *tee;
+
+ tee = DLCALL (cairo_get_target, cr);
+ return fdr_tee_surface_index (tee, 0);
+}
+
+cairo_surface_t *
+cairo_get_group_target (cairo_t *cr)
+{
+ cairo_surface_t *tee;
+
+ tee = DLCALL (cairo_get_group_target, cr);
+ return fdr_tee_surface_index (tee, 0);
+}
+
+cairo_pattern_t *
+cairo_pattern_create_for_surface (cairo_surface_t *surface)
+{
+ cairo_surface_t *tee;
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee != NULL)
+ surface = tee;
+
+ return DLCALL (cairo_pattern_create_for_surface, surface);
+}
+
+cairo_status_t
+cairo_pattern_get_surface (cairo_pattern_t *pattern,
+ cairo_surface_t **surface)
+{
+ cairo_status_t status;
+ cairo_surface_t *tee;
+
+ status = DLCALL (cairo_pattern_get_surface, pattern, surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ return status;
+
+ tee = fdr_surface_get_tee (*surface);
+ if (tee != NULL)
+ *surface = tee;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+cairo_set_source_surface (cairo_t *cr,
+ cairo_surface_t *surface,
+ double x, double y)
+{
+ cairo_surface_t *tee;
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee != NULL)
+ surface = tee;
+
+ DLCALL (cairo_set_source_surface, cr, surface, x, y);
+}
+
+cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t *surface,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_surface_t *tee;
+
+ tee = fdr_surface_get_tee (surface);
+ if (tee != NULL)
+ surface = tee;
+
+ return DLCALL (cairo_surface_create_similar,
+ surface, content, width, height);
+}
diff --git a/util/cairo-sphinx/sphinx.c b/util/cairo-sphinx/sphinx.c
new file mode 100644
index 000000000..238d40064
--- /dev/null
+++ b/util/cairo-sphinx/sphinx.c
@@ -0,0 +1,1545 @@
+/*
+ * The intention for sphinx is for detection of rendering errors inside
+ * applications by simultaneously rendering on to the target device and on
+ * an image surface and comparing the two. If it found a discrepancy, it
+ * would then dump the trace that reproduces the error. (Then apply
+ * delta-debugging to reduce that down to a minimal trace.)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include <cairo.h>
+#include <cairo-script.h>
+#include <cairo-script-interpreter.h>
+#include <cairo-boilerplate.h>
+
+#include <glib.h> /* for checksumming */
+
+#ifndef CAIRO_HAS_REAL_PTHREAD
+# error "cairo-sphinx needs real pthreads"
+#endif
+
+#ifndef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
+#define DATA_SIZE (256 << 20)
+#define SHM_PATH_XXX "/shmem-cairo-sphinx"
+
+struct client {
+ int sk;
+ const cairo_boilerplate_target_t *target;
+ cairo_surface_t *surface;
+ void *base;
+
+ cairo_script_interpreter_t *csi;
+ struct context_closure {
+ struct context_closure *next;
+ unsigned long id;
+ cairo_t *context;
+ cairo_surface_t *surface;
+ cairo_surface_t *original;
+ } *contexts;
+
+ unsigned long context_id;
+};
+
+struct surface_tag {
+ long width, height;
+};
+static const cairo_user_data_key_t surface_tag;
+
+static int
+client_socket (const char *socket_path);
+
+static int
+writen (int fd, const void *ptr, int len)
+{
+#if 1
+ const uint8_t *data = ptr;
+ while (len) {
+ int ret = write (fd, data, len);
+ if (ret < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ continue;
+ default:
+ return FALSE;
+ }
+ } else if (ret == 0) {
+ return FALSE;
+ } else {
+ data += ret;
+ len -= ret;
+ }
+ }
+ return TRUE;
+#else
+ int ret = send (fd, ptr, len, 0);
+ return ret == len;
+#endif
+}
+
+static int
+readn (int fd, void *ptr, int len)
+{
+#if 0
+ uint8_t *data = ptr;
+ while (len) {
+ int ret = read (fd, data, len);
+ if (ret < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ continue;
+ default:
+ return FALSE;
+ }
+ } else if (ret == 0) {
+ return FALSE;
+ } else {
+ data += ret;
+ len -= ret;
+ }
+ }
+ return TRUE;
+#else
+ int ret = recv (fd, ptr, len, MSG_WAITALL);
+ return ret == len;
+#endif
+}
+static int
+open_devnull_to_fd (int want_fd, int flags)
+{
+ int error;
+ int got_fd;
+
+ close (want_fd);
+
+ got_fd = open("/dev/null", flags | O_CREAT, 0700);
+ if (got_fd == -1)
+ return -1;
+
+ error = dup2 (got_fd, want_fd);
+ close (got_fd);
+
+ return error;
+}
+
+static int
+daemonize (void)
+{
+ void (*oldhup) (int);
+
+ /* Let the parent go. */
+ switch (fork ()) {
+ case -1: return -1;
+ case 0: break;
+ default: _exit (0);
+ }
+
+ /* Become session leader. */
+ if (setsid () == -1)
+ return -1;
+
+ /* Refork to yield session leadership. */
+ oldhup = signal (SIGHUP, SIG_IGN);
+ switch (fork ()) {
+ case -1: return -1;
+ case 0: break;
+ default: _exit (0);
+ }
+ signal (SIGHUP, oldhup);
+
+ /* Establish stdio. */
+ if (open_devnull_to_fd (0, O_RDONLY) == -1)
+ return -1;
+ if (open_devnull_to_fd (1, O_WRONLY | O_APPEND) == -1)
+ return -1;
+ if (dup2 (1, 2) == -1)
+ return -1;
+
+ return 0;
+}
+
+static int
+server_socket (const char *socket_path)
+{
+ long flags;
+ struct sockaddr_un addr;
+ int sk;
+
+ unlink (socket_path);
+
+ sk = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (sk == -1)
+ return -1;
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sun_family = AF_UNIX;
+ strcpy (addr.sun_path, socket_path);
+ if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
+ close (sk);
+ return -1;
+ }
+
+ flags = fcntl (sk, F_GETFL);
+ if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
+ close (sk);
+ return -1;
+ }
+
+ if (listen (sk, 5) == -1) {
+ close (sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+static int
+readline (int fd, char *line, int max)
+{
+ int len = 0;
+ do {
+ int ret = read (fd, &line[len], 1);
+ if (ret <= 0)
+ return -1;
+ } while (line[len] != '\n' && ++len < max);
+ line[len] = '\0';
+ return len;
+}
+
+struct clients {
+ int count, size;
+ int complete;
+
+ cairo_surface_t *recording;
+ unsigned long serial;
+
+ struct client_info {
+ int sk;
+ int trace;
+ unsigned long image_serial;
+ cairo_surface_t *image;
+ char *name;
+ char *target;
+ char *reference;
+
+ uint8_t *out_buf;
+ int out_len;
+ int out_size;
+ } *clients;
+ const char *shm_path;
+ unsigned long offset;
+ uint8_t *base;
+};
+
+static void *
+clients_shm (const char *shm_path)
+{
+ void *base;
+ int fd;
+
+ shm_unlink (shm_path);
+ fd = shm_open (shm_path, O_RDWR | O_EXCL | O_CREAT, 0777);
+ if (fd == -1)
+ return MAP_FAILED;
+
+ if (ftruncate (fd, DATA_SIZE) == -1) {
+ close (fd);
+ return MAP_FAILED;
+ }
+
+ base = mmap (NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ close (fd);
+
+ return base;
+}
+
+static int
+clients_init (struct clients *clients)
+{
+ clients->count = 0;
+ clients->complete = 0;
+ clients->size = 4;
+ clients->clients = xmalloc (clients->size * sizeof (struct client_info));
+
+ clients->shm_path = SHM_PATH_XXX;
+ clients->base = clients_shm (clients->shm_path);
+ if (clients->base == MAP_FAILED)
+ return -1;
+ clients->offset = 0;
+
+ clients->recording = NULL;
+ clients->serial = 0;
+
+ return 0;
+}
+
+static void
+clients_add_command (struct clients *clients, int fd, char *info)
+{
+ struct client_info *c;
+ char buf[1024];
+ int len;
+ char *str;
+
+ if (clients->count == clients->size) {
+ clients->size *= 2;
+ clients->clients = xrealloc (clients->clients,
+ clients->size * sizeof (struct client_info));
+ }
+
+ c = &clients->clients[clients->count++];
+ c->sk = fd;
+ c->trace = -1;
+ c->image_serial = 0;
+ c->image = NULL;
+ c->name = c->target = c->reference = NULL;
+
+ c->out_size = 8192;
+ c->out_buf = xmalloc (c->out_size);
+ c->out_len = 0;
+
+ str = strstr (info, "name=");
+ if (str != NULL) {
+ char *sp = strchr (str + 5, ' ');
+ int len;
+ if (sp)
+ len = sp - str - 5;
+ else
+ len = strlen (str + 5);
+ c->name = xmalloc (len + 1);
+ memcpy (c->name, str + 5, len);
+ c->name[len] = '\0';
+ }
+
+ str = strstr (info, "target=");
+ if (str != NULL) {
+ char *sp = strchr (str + 7, ' ');
+ int len;
+ if (sp)
+ len = sp - str - 7;
+ else
+ len = strlen (str + 7);
+ c->target = xmalloc (len + 1);
+ memcpy (c->target, str + 7, len);
+ c->target[len] = '\0';
+ }
+
+ str = strstr (info, "reference=");
+ if (str != NULL) {
+ char *sp = strchr (str + 10, ' ');
+ int len;
+ if (sp)
+ len = sp - str - 10;
+ else
+ len = strlen (str + 10);
+ c->reference = xmalloc (len + 1);
+ memcpy (c->reference, str + 10, len);
+ c->reference[len] = '\0';
+ }
+
+ len = sprintf (buf, "%s\n", clients->shm_path);
+ writen (fd, buf, len);
+}
+
+static void
+clients_add_trace (struct clients *clients, int fd, char *info)
+{
+ char *str, *sp;
+ char *name;
+ int i;
+
+ str = strstr (info, "name=");
+ assert (str != NULL);
+ sp = strchr (str + 5, ' ');
+ if (sp)
+ i = sp - str - 5;
+ else
+ i = strlen (str + 5);
+
+ name = xmalloc (i + 1);
+ memcpy (name, str + 5, i);
+ name[i] = '\0';
+
+ for (i = 0; i < clients->count; i++) {
+ struct client_info *c = &clients->clients[i];
+ if (strcmp (name, c->name) == 0) {
+ c->trace = fd;
+ break;
+ }
+ }
+
+ free (name);
+}
+
+static int
+clients_image (struct clients *clients, int fd, char *info)
+{
+ struct client_info *c = NULL;
+ int format, width, height, stride, size;
+ int i;
+
+ for (i = 0; i < clients->count; i++) {
+ if (clients->clients[i].sk == fd) {
+ c = &clients->clients[i];
+ break;
+ }
+ }
+
+ if (c == NULL)
+ return 0;
+
+ if (sscanf (info, "%lu %d %d %d %d",
+ &c->image_serial, &format, &width, &height, &stride) != 5)
+ {
+ return 0;
+ }
+
+ size = height * stride;
+ size = (size + 4095) & -4096;
+ assert (clients->offset + size <= DATA_SIZE);
+
+ c->image =
+ cairo_image_surface_create_for_data (clients->base + clients->offset,
+ format, width, height, stride);
+
+ if (! writen (fd, &clients->offset, sizeof (clients->offset)))
+ return 0;
+
+ clients->offset += size;
+
+ return 1;
+}
+
+static int
+u8_cmp (const void *A, const void *B)
+{
+ const uint8_t *a = A, *b = B;
+ return (int) *a - (int) *b;
+}
+
+static uint8_t
+median (uint8_t *values, int count)
+{
+ /* XXX could use a fast median here if we cared */
+ qsort (values, count, 1, u8_cmp);
+ return values[count/2];
+}
+
+static uint32_t
+get_pixel32 (int x, int y, const uint8_t *data, int stride)
+{
+ return ((uint32_t *)(data + y * stride))[x];
+}
+
+static uint8_t
+get_median_32 (int x, int y, int channel,
+ const uint8_t *data, int width, int height, int stride)
+{
+ uint8_t neighbourhood[25];
+ int cnt = 0;
+ int xx, yy;
+
+ for (yy = y - 2; yy <= y + 2; yy++) {
+ if (yy < 0)
+ continue;
+ if (yy >= height)
+ continue;
+
+ for (xx = x - 2; xx <= x + 2; xx++) {
+ if (xx < 0)
+ continue;
+ if (xx >= width)
+ continue;
+
+ neighbourhood[cnt++] = (get_pixel32 (xx, yy, data, stride) >> (channel*8)) & 0xff;
+ }
+ }
+
+ return median (neighbourhood, cnt);
+}
+
+static uint8_t
+get_pixel8 (int x, int y, const uint8_t *data, int stride)
+{
+ return data[y * stride + x];
+}
+
+static uint8_t
+get_median_8 (int x, int y, const uint8_t *data, int width, int height, int stride)
+{
+ uint8_t neighbourhood[25];
+ int cnt = 0;
+ int xx, yy;
+
+ for (yy = y - 2; yy <= y + 2; yy++) {
+ if (yy < 0)
+ continue;
+ if (yy >= height)
+ continue;
+
+ for (xx = x - 2; xx <= x + 2; xx++) {
+ if (xx < 0)
+ continue;
+ if (xx >= width)
+ continue;
+
+ neighbourhood[cnt++] = get_pixel8 (xx, yy, data, stride);
+ }
+ }
+
+ return median (neighbourhood, cnt);
+}
+
+static cairo_bool_t
+compare_images (cairo_surface_t *a,
+ cairo_surface_t *b)
+{
+ int width, height, stride;
+ const uint8_t *aa, *bb;
+ int x, y;
+
+ if (cairo_surface_status (a) || cairo_surface_status (b))
+ return FALSE;
+
+ if (cairo_surface_get_type (a) != cairo_surface_get_type (b))
+ return FALSE;
+
+ if (cairo_image_surface_get_format (a) != cairo_image_surface_get_format (b))
+ return FALSE;
+
+ if (cairo_image_surface_get_width (a) != cairo_image_surface_get_width (b))
+ return FALSE;
+
+ if (cairo_image_surface_get_height (a) != cairo_image_surface_get_height (b))
+ return FALSE;
+
+ if (cairo_image_surface_get_stride (a) != cairo_image_surface_get_stride (b))
+ return FALSE;
+
+
+ width = cairo_image_surface_get_width (a);
+ height = cairo_image_surface_get_height (a);
+ stride = cairo_image_surface_get_stride (a);
+
+ aa = cairo_image_surface_get_data (a);
+ bb = cairo_image_surface_get_data (b);
+ switch (cairo_image_surface_get_format (a)) {
+ case CAIRO_FORMAT_ARGB32:
+ for (y = 0; y < height; y++) {
+ const uint32_t *ua = (uint32_t *) aa;
+ const uint32_t *ub = (uint32_t *) bb;
+ for (x = 0; x < width; x++) {
+ if (ua[x] != ub[x]) {
+ int channel;
+
+ for (channel = 0; channel < 4; channel++) {
+ unsigned va, vb, diff;
+
+ va = (ua[x] >> (channel*8)) & 0xff;
+ vb = (ub[x] >> (channel*8)) & 0xff;
+ diff = abs (va - vb);
+ if (diff > 1) {
+ va = get_median_32 (x, y, channel, aa, width, height, stride);
+ vb = get_median_32 (x, y, channel, bb, width, height, stride);
+ diff = abs (va - vb);
+ if (diff > 1)
+ return FALSE;
+ }
+ }
+ }
+ }
+ aa += stride;
+ bb += stride;
+ }
+ break;
+
+ case CAIRO_FORMAT_RGB24:
+ for (y = 0; y < height; y++) {
+ const uint32_t *ua = (uint32_t *) aa;
+ const uint32_t *ub = (uint32_t *) bb;
+ for (x = 0; x < width; x++) {
+ if ((ua[x] & 0x00ffffff) != (ub[x] & 0x00ffffff)) {
+ int channel;
+
+ for (channel = 0; channel < 3; channel++) {
+ unsigned va, vb, diff;
+
+ va = (ua[x] >> (channel*8)) & 0xff;
+ vb = (ub[x] >> (channel*8)) & 0xff;
+ diff = abs (va - vb);
+ if (diff > 1) {
+ va = get_median_32 (x, y, channel, aa, width, height, stride);
+ vb = get_median_32 (x, y, channel, bb, width, height, stride);
+ diff = abs (va - vb);
+ if (diff > 1)
+ return FALSE;
+ }
+ }
+ }
+ }
+ aa += stride;
+ bb += stride;
+ }
+ break;
+
+ case CAIRO_FORMAT_A8:
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ if (aa[x] != bb[x]) {
+ unsigned diff = abs (aa[x] - bb[x]);
+ if (diff > 1) {
+ uint8_t va, vb;
+
+ va = get_median_8 (x, y, aa, width, height, stride);
+ vb = get_median_8 (x, y, bb, width, height, stride);
+ diff = abs (va - vb);
+ if (diff > 1)
+ return FALSE;
+ }
+
+ }
+ }
+ aa += stride;
+ bb += stride;
+ }
+ break;
+
+ case CAIRO_FORMAT_A1:
+ width /= 8;
+ for (y = 0; y < height; y++) {
+ if (memcmp (aa, bb, width))
+ return FALSE;
+ aa += stride;
+ bb += stride;
+ }
+ break;
+
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_RGB16_565: /* XXX */
+ break;
+ }
+
+ return TRUE;
+}
+
+static int
+check_images (struct clients *clients)
+{
+ int i, j;
+
+ for (i = 0; i < clients->count; i++) {
+ struct client_info *c = &clients->clients[i];
+
+ if (c->reference == NULL)
+ continue;
+
+ for (j = 0; j < clients->count; j++) {
+ struct client_info *ref = &clients->clients[j];
+
+ if (strcmp (c->reference, ref->name))
+ continue;
+
+ if (! compare_images (c->image, ref->image))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static gchar *
+checksum (const char *filename)
+{
+ gchar *str = NULL;
+ gchar *data;
+ gsize len;
+
+ if (g_file_get_contents (filename, &data, &len, NULL)) {
+ str = g_compute_checksum_for_data (G_CHECKSUM_SHA1, (guchar *) data, len);
+ g_free (data);
+ }
+
+ return str;
+}
+
+static void
+write_trace (struct clients *clients)
+{
+ cairo_device_t *ctx;
+ gchar *csum;
+ char buf[4096];
+ int i;
+
+ mkdir ("output", 0777);
+
+ ctx = cairo_script_create ("output/cairo-sphinx.trace");
+ cairo_script_from_recording_surface (ctx, clients->recording);
+ cairo_device_destroy (ctx);
+
+ csum = checksum ("output/cairo-sphinx.trace");
+
+ sprintf (buf, "output/%s.trace", csum);
+ if (! g_file_test (buf, G_FILE_TEST_EXISTS)) {
+ rename ("output/cairo-sphinx.trace", buf);
+
+ sprintf (buf, "output/%s.recording.png", csum);
+ cairo_surface_write_to_png (clients->recording, buf);
+
+ for (i = 0; i < clients->count; i++) {
+ struct client_info *c = &clients->clients[i];
+ if (c->image != NULL) {
+ sprintf (buf, "output/%s.%s.png", csum, c->name);
+ cairo_surface_write_to_png (c->image, buf);
+ }
+ }
+ }
+}
+
+static void
+clients_complete (struct clients *clients, int fd)
+{
+ int i;
+
+ for (i = 0; i < clients->count; i++) {
+ if (clients->clients[i].sk == fd) {
+ break;
+ }
+ }
+ if (i == clients->count)
+ return;
+
+ if (++clients->complete != clients->count)
+ return;
+
+ clients->offset = 0;
+ clients->complete = 0;
+
+ if (! check_images (clients))
+ write_trace (clients);
+
+ /* ack */
+ for (i = 0; i < clients->count; i++) {
+ struct client_info *c = &clients->clients[i];
+
+ cairo_surface_destroy (c->image);
+ c->image = NULL;
+
+ if (! writen (c->sk, &clients->serial, sizeof (clients->serial)))
+ continue;
+
+ c->image_serial = 0;
+ }
+
+ clients->recording = NULL;
+ clients->serial = 0;
+}
+
+static void
+clients_recording (struct clients *clients, int fd, char *info)
+{
+ sscanf (info, "%p %lu", &clients->recording, &clients->serial);
+ clients_complete (clients, fd);
+}
+
+static void
+clients_remove (struct clients *clients, int fd)
+{
+ int i, j;
+
+ for (i = 0; i < clients->count; i++) {
+ struct client_info *c = &clients->clients[i];
+ if (c->sk == fd) {
+ free (c->out_buf);
+ break;
+ }
+ }
+
+ for (j = i++; i < clients->count; i++)
+ clients->clients[j] = clients->clients[i];
+
+ clients->count = j;
+}
+
+static void
+clients_send_trace (struct clients *clients,
+ const char * const line, const int len)
+{
+ int i;
+
+ for (i = 0; i < clients->count; i++) {
+ struct client_info *c = &clients->clients[i];
+ int ret, rem = len;
+
+ if (c->trace == -1)
+ continue;
+
+ if (c->out_len) {
+ ret = write (c->trace, c->out_buf, c->out_len);
+ if (ret > 0) {
+ c->out_len -= ret;
+ if (c->out_len)
+ memmove (c->out_buf, c->out_buf + ret, c->out_len);
+ }
+ }
+
+ if (! c->out_len) {
+ ret = write (c->trace, line, rem);
+ if (ret > 0)
+ rem -= ret;
+ }
+
+ if (rem) {
+ if (c->out_len + rem > c->out_size) {
+ c->out_size *= 2;
+ c->out_buf = xrealloc (c->out_buf, c->out_size);
+ }
+
+ memcpy (c->out_buf + c->out_len, line, rem);
+ c->out_len += rem;
+ }
+ }
+}
+
+static void
+clients_fini (struct clients *clients)
+{
+ shm_unlink (clients->shm_path);
+ munmap (clients->base, DATA_SIZE);
+ free (clients->clients);
+}
+
+static int
+nonblocking (int fd)
+{
+ long flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (flags == -1)
+ return -1;
+
+ return fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+static void *
+request_image (struct client *c,
+ struct context_closure *closure,
+ cairo_format_t format,
+ int width, int height, int stride)
+{
+ char buf[1024];
+ unsigned long offset = -1;
+ int len;
+
+ assert (format != CAIRO_FORMAT_INVALID);
+
+ len = sprintf (buf, ".image %lu %d %d %d %d\n",
+ closure->id, format, width, height, stride);
+ writen (c->sk, buf, len);
+
+ readn (c->sk, &offset, sizeof (offset));
+ if (offset == (unsigned long) -1)
+ return NULL;
+
+ return (uint8_t *) c->base + offset;
+}
+
+static cairo_format_t
+format_for_content (cairo_content_t content)
+{
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ return CAIRO_FORMAT_A8;
+ case CAIRO_CONTENT_COLOR:
+ return CAIRO_FORMAT_RGB24;
+ default:
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ return CAIRO_FORMAT_ARGB32;
+ }
+}
+
+static void
+get_surface_size (cairo_surface_t *surface,
+ int *width, int *height,
+ cairo_format_t *format)
+{
+ if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE) {
+ *width = cairo_image_surface_get_width (surface);
+ *height = cairo_image_surface_get_height (surface);
+ *format = cairo_image_surface_get_format (surface);
+ } else {
+ struct surface_tag *tag;
+
+ tag = cairo_surface_get_user_data (surface, &surface_tag);
+ if (tag != NULL) {
+ *width = tag->width;
+ *height = tag->height;
+ } else {
+ double x0, x1, y0, y1;
+ cairo_t *cr;
+
+ /* presumably created using cairo_surface_create_similar() */
+ cr = cairo_create (surface);
+ cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
+ cairo_destroy (cr);
+
+ tag = xmalloc (sizeof (*tag));
+ *width = tag->width = ceil (x1 - x0);
+ *height = tag->height = ceil (y1 - y0);
+
+ if (cairo_surface_set_user_data (surface, &surface_tag, tag, free))
+ exit (-1);
+ }
+ }
+}
+
+
+static void
+send_surface (struct client *c,
+ struct context_closure *closure)
+{
+ cairo_surface_t *source = closure->surface;
+ cairo_surface_t *image;
+ cairo_format_t format = CAIRO_FORMAT_INVALID;
+ cairo_t *cr;
+ int width, height, stride;
+ void *data;
+ unsigned long serial;
+
+ get_surface_size (source, &width, &height, &format);
+ if (format == CAIRO_FORMAT_INVALID)
+ format = format_for_content (cairo_surface_get_content (source));
+
+ stride = cairo_format_stride_for_width (format, width);
+
+ data = request_image (c, closure, format, width, height, stride);
+ if (data == NULL)
+ exit (-1);
+
+ image = cairo_image_surface_create_for_data (data,
+ format,
+ width, height,
+ stride);
+ cr = cairo_create (image);
+ cairo_surface_destroy (image);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface (cr, source, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ /* signal completion */
+ writen (c->sk, ".complete\n", strlen (".complete\n"));
+
+ /* wait for image check */
+ serial = 0;
+ readn (c->sk, &serial, sizeof (serial));
+ if (serial != closure->id)
+ exit (-1);
+}
+
+static void
+send_recording (struct client *c,
+ struct context_closure *closure)
+{
+ cairo_surface_t *source = closure->surface;
+ char buf[1024];
+ int len;
+ unsigned long serial;
+
+ assert (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_RECORDING);
+ len = sprintf (buf, ".recording %p %lu\n", source, closure->id);
+ writen (c->sk, buf, len);
+
+ /* wait for image check */
+
+ serial = 0;
+ readn (c->sk, &serial, sizeof (serial));
+ if (serial != closure->id)
+ exit (-1);
+}
+
+static cairo_surface_t *
+_surface_create (void *closure,
+ cairo_content_t content,
+ double width, double height,
+ long uid)
+{
+ struct client *c = closure;
+ cairo_surface_t *surface;
+
+ surface = cairo_surface_create_similar (c->surface,
+ content, width, height);
+ if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE) {
+ struct surface_tag *tag;
+
+ tag = xmalloc (sizeof (*tag));
+ tag->width = width;
+ tag->height = height;
+ if (cairo_surface_set_user_data (surface, &surface_tag, tag, free))
+ exit (-1);
+ }
+
+ return surface;
+}
+
+static cairo_t *
+_context_create (void *closure, cairo_surface_t *surface)
+{
+ struct client *c = closure;
+ struct context_closure *l;
+ cairo_bool_t foreign = FALSE;
+
+ l = xmalloc (sizeof (*l));
+ l->next = c->contexts;
+ l->surface = surface;
+ l->original = cairo_surface_reference (surface);
+ l->id = ++c->context_id;
+ if (l->id == 0)
+ l->id = ++c->context_id;
+ c->contexts = l;
+
+ /* record everything, including writes to images */
+ if (c->target == NULL) {
+ if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_RECORDING) {
+ cairo_format_t format;
+ int width, height;
+
+ get_surface_size (surface, &width, &height, &format);
+ l->surface = cairo_surface_create_similar (c->surface,
+ cairo_surface_get_content (surface),
+ width, height);
+ foreign = TRUE;
+ }
+ }
+
+ l->context = cairo_create (l->surface);
+ if (foreign) {
+ cairo_set_source_surface (l->context, surface, 0, 0);
+ cairo_paint (l->context);
+ }
+
+ return l->context;
+}
+
+static void
+_context_destroy (void *closure, void *ptr)
+{
+ struct client *c = closure;
+ struct context_closure *l, **prev = &c->contexts;
+
+ while ((l = *prev) != NULL) {
+ if (l->context == ptr) {
+ if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) {
+ if (c->target == NULL)
+ send_recording (c, l);
+ else
+ send_surface (c, l);
+ } else {
+ exit (-1);
+ }
+
+ cairo_surface_destroy (l->original);
+ *prev = l->next;
+ free (l);
+ return;
+ }
+ prev = &l->next;
+ }
+}
+
+static void *
+recorder (void *arg)
+{
+ struct client client;
+ const cairo_script_interpreter_hooks_t hooks = {
+ .closure = &client,
+ .surface_create = _surface_create,
+ .context_create = _context_create,
+ .context_destroy = _context_destroy,
+ };
+ char *buf;
+ int buf_size;
+ int len = 0, ret;
+ struct pollfd pfd;
+
+ client.target = NULL;
+ client.sk = client_socket ("/tmp/cairo-sphinx");
+ if (client.sk < 0)
+ return NULL;
+
+ buf_size = 65536;
+ buf = xmalloc (buf_size);
+
+ len = sprintf (buf, "client-command target=recording name=.recorder\n");
+ if (! writen (client.sk, buf, len))
+ return NULL;
+
+ /* drain the shm_path */
+ len = readline (client.sk, buf, buf_size);
+
+ pfd.fd = client_socket ("/tmp/cairo-sphinx");
+ if (pfd.fd < 0)
+ return NULL;
+
+ len = sprintf (buf, "client-trace name=.recorder\n");
+ if (! writen (pfd.fd, buf, len))
+ return NULL;
+
+ client.surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
+ NULL);
+
+ client.context_id = 0;
+ client.csi = cairo_script_interpreter_create ();
+ cairo_script_interpreter_install_hooks (client.csi, &hooks);
+
+ nonblocking (pfd.fd);
+ pfd.events = POLLIN;
+ len = 0;
+ while (poll (&pfd, 1, -1) > 0) {
+ while ((ret = read (pfd.fd, buf + len, buf_size - len)) > 0) {
+ int end;
+
+ if (ret == buf_size - len) {
+ buf_size *= 2;
+ buf = xrealloc (buf, buf_size);
+ }
+ len += ret;
+
+ for (end = len; end > 0 && buf[--end] != '\n'; )
+ ;
+ if (end > 0) {
+ buf[end] = '\0';
+ cairo_script_interpreter_feed_string (client.csi, buf, end);
+
+ len -= end + 1;
+ if (len)
+ memmove (buf, buf + end + 1, len);
+ }
+ }
+ if (ret == 0)
+ break;
+ if (! (errno == EAGAIN || errno == EINTR))
+ break;
+ }
+
+ cairo_script_interpreter_finish (client.csi);
+ cairo_script_interpreter_destroy (client.csi);
+
+ cairo_surface_destroy (client.surface);
+ return NULL;
+}
+
+static int
+do_server (const char *path)
+{
+ pthread_t thread;
+ struct clients clients;
+ char line[4096];
+ struct pollfd *pfd;
+ int num_pfd, size_pfd;
+ int n, cnt, ret = 1;
+ int sk, source = -1;
+ int waiter = -1, waiter_count = 0;
+ int len;
+
+ signal (SIGPIPE, SIG_IGN);
+
+ if (clients_init (&clients) < 0) {
+ fprintf (stderr, "Failed to initialise clients structure\n");
+ return -1;
+ }
+
+ sk = server_socket (path);
+ if (sk < 0) {
+ fprintf (stderr, "Failed to create server socket\n");
+ return 1;
+ }
+
+ if (daemonize () < 0)
+ return 1;
+
+ if (pthread_create (&thread, NULL, recorder, NULL) < 0) {
+ fprintf (stderr, "Failed to create spawn recording thread\n");
+ return 1;
+ }
+
+ size_pfd = 4;
+ pfd = xmalloc (sizeof (*pfd) * size_pfd);
+ pfd[0].fd = sk;
+ pfd[0].events = POLLIN;
+ num_pfd = 1;
+
+ while ((cnt = poll (pfd, num_pfd, -1)) > 0) {
+ int have_source;
+
+ if (pfd[0].revents) {
+ while ((sk = accept (pfd[0].fd, NULL, NULL)) != -1) {
+ len = readline (sk, line, sizeof (line));
+ if (strcmp (line, "source") == 0) {
+
+ if (source != -1)
+ exit (1);
+
+ source = sk;
+ if (nonblocking (sk) < 0) {
+ close (sk);
+ continue;
+ }
+ } else if (strncmp (line, "client-command", 14) == 0) {
+ if (source == -1)
+ clients_add_command (&clients, sk, line);
+ } else if (strncmp (line, "client-trace", 12) == 0) {
+ if (source == -1) {
+ clients_add_trace (&clients, sk, line);
+ if (nonblocking (sk) < 0) {
+ close (sk);
+ continue;
+ }
+
+ if (clients.count == waiter_count) {
+ for (n = 1; n < num_pfd; n++) {
+ if (pfd[n].fd == waiter) {
+ pfd[n].fd = -1;
+ break;
+ }
+ }
+ close (waiter);
+ waiter_count = -1;
+ }
+ }
+ } else if (strncmp (line, "wait", 4) == 0) {
+ int count = atoi (line + 5) + 1;
+ if (clients.count == count) {
+ close (sk);
+ continue;
+ } else {
+ waiter = sk;
+ waiter_count = count;
+ }
+ }
+
+ if (num_pfd == size_pfd) {
+ size_pfd *= 2;
+ pfd = xrealloc (pfd, sizeof (*pfd) * size_pfd);
+ }
+
+ pfd[num_pfd].fd = sk;
+ pfd[num_pfd].events = POLLIN;
+ pfd[num_pfd].revents = 0;
+ num_pfd++;
+ }
+ cnt--;
+ }
+
+ have_source = 0;
+ for (n = 1; cnt && n < num_pfd; n++) {
+ if (! pfd[n].revents)
+ continue;
+ cnt--;
+
+ if (pfd[n].fd == -1)
+ continue;
+
+ if (source == pfd[n].fd) {
+ have_source = n;
+ } else {
+ len = readline (pfd[n].fd, line, sizeof (line));
+ if (len < 0) {
+ clients_remove (&clients, pfd[n].fd);
+ close (pfd[n].fd);
+ pfd[n].fd = -1;
+ continue;
+ }
+
+ if (strncmp (line, ".image", 6) == 0) {
+ if (! clients_image (&clients, pfd[n].fd, line + 7)) {
+ clients_remove (&clients, pfd[n].fd);
+ close (pfd[n].fd);
+ pfd[n].fd = -1;
+ continue;
+ }
+ } else if (strncmp (line, ".complete", 9) == 0) {
+ clients_complete (&clients, pfd[n].fd);
+ } else if (strncmp (line, ".recording", 10) == 0) {
+ clients_recording (&clients, pfd[n].fd, line + 6);
+ } else {
+ printf ("do_command (%s)\n", line);
+ }
+ }
+ }
+
+ if (have_source) {
+ do {
+ len = read (source, line, sizeof (line));
+ if (len > 0) {
+ clients_send_trace (&clients, line, len);
+ } else if (len == 0) {
+ close (source);
+ pfd[have_source].fd = source = -1;
+ goto done;
+ } else
+ break;
+ } while (1);
+ }
+
+ for (n = cnt = 1; n < num_pfd; n++) {
+ if (pfd[n].fd != -1) {
+ if (cnt != n)
+ pfd[cnt] = pfd[n];
+ cnt++;
+ }
+ }
+ num_pfd = cnt;
+ }
+
+done:
+ ret = 0;
+ for (n = 0; n < num_pfd; n++) {
+ if (pfd[n].fd != -1)
+ close (pfd[n].fd);
+ }
+ free (pfd);
+ clients_fini (&clients);
+
+ return ret;
+}
+
+static void *
+client_shm (const char *shm_path)
+{
+ void *base;
+ int fd;
+
+ fd = shm_open (shm_path, O_RDWR, 0);
+ if (fd == -1)
+ return MAP_FAILED;
+
+ base = mmap (NULL, DATA_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_NORESERVE,
+ fd, 0);
+ close (fd);
+
+ return base;
+}
+
+static int
+client_socket (const char *socket_path)
+{
+ struct sockaddr_un addr;
+ int sk;
+
+ sk = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (sk == -1)
+ return -1;
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sun_family = AF_UNIX;
+ strcpy (addr.sun_path, socket_path);
+
+ if (connect (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1)
+ return -1;
+
+ return sk;
+}
+
+static int
+do_client (int fd,
+ const char *target,
+ const char *name,
+ const char *reference,
+ cairo_content_t content)
+{
+ struct client client;
+ const cairo_script_interpreter_hooks_t hooks = {
+ .closure = &client,
+ .surface_create = _surface_create,
+ .context_create = _context_create,
+ .context_destroy = _context_destroy,
+ };
+ void *closure;
+ char *buf;
+ int buf_size;
+ int len = 0, ret;
+ struct pollfd pfd;
+
+ client.sk = fd;
+ client.target = cairo_boilerplate_get_target_by_name (target, content);
+ client.context_id = 0;
+
+ client.surface = client.target->create_surface (NULL, content, 1, 1, 1, 1,
+ CAIRO_BOILERPLATE_MODE_TEST,
+ &closure);
+ if (client.surface == NULL) {
+ fprintf (stderr, "Failed to create target surface: %s.\n",
+ client.target->name);
+ return 1;
+ }
+
+ buf_size = 65536;
+ buf = xmalloc (buf_size);
+
+ if (reference != NULL) {
+ len = sprintf (buf,
+ "client-command name=%s target=%s reference=%s\n",
+ name, target, reference);
+ } else {
+ len = sprintf (buf,
+ "client-command name=%s target=%s\n",
+ name, target);
+ }
+ if (! writen (fd, buf, len))
+ return 1;
+
+ len = readline (fd, buf, buf_size);
+ client.base = client_shm (buf);
+ if (client.base == MAP_FAILED) {
+ fprintf (stderr, "Failed to map shared memory segment '%s'.\n", buf);
+ return 1;
+ }
+
+ if (daemonize () < 0)
+ return 1;
+
+ pfd.fd = client_socket ("/tmp/cairo-sphinx");
+ if (pfd.fd < 0)
+ return 1;
+
+ len = sprintf (buf, "client-trace name=%s\n", name);
+ if (! writen (pfd.fd, buf, len))
+ return 1;
+
+ client.csi = cairo_script_interpreter_create ();
+ cairo_script_interpreter_install_hooks (client.csi, &hooks);
+
+ nonblocking (pfd.fd);
+ pfd.events = POLLIN;
+ len = 0;
+ while (poll (&pfd, 1, -1) > 0) {
+ while ((ret = read (pfd.fd, buf + len, buf_size - len)) > 0) {
+ int end;
+
+ if (ret == buf_size - len) {
+ buf_size *= 2;
+ buf = xrealloc (buf, buf_size);
+ }
+ len += ret;
+
+ for (end = len; end > 0 && buf[--end] != '\n'; )
+ ;
+ if (end > 0) {
+ buf[end] = '\0';
+ cairo_script_interpreter_feed_string (client.csi, buf, end);
+
+ len -= end + 1;
+ if (len)
+ memmove (buf, buf + end + 1, len);
+ }
+ }
+ if (ret == 0)
+ break;
+ if (! (errno == EAGAIN || errno == EINTR))
+ break;
+ }
+
+ cairo_script_interpreter_finish (client.csi);
+ cairo_script_interpreter_destroy (client.csi);
+
+ cairo_surface_destroy (client.surface);
+ close (fd);
+
+ return 0;
+}
+
+static int
+do_exec (int fd, char **argv)
+{
+ char buf[4096];
+
+ if (*argv == NULL)
+ return 0;
+
+ snprintf (buf, sizeof (buf), "%s/cairo-trace.so", LIBDIR);
+ setenv ("LD_PRELOAD", buf, 1);
+
+ snprintf (buf, sizeof (buf), "0");
+ setenv ("CAIRO_TRACE_LINE_INFO", buf, 1);
+
+ snprintf (buf, sizeof (buf), "%d", fd);
+ setenv ("CAIRO_TRACE_FD", buf, 1);
+ putenv (buf);
+
+ return execvp (argv[0], argv);
+}
+
+static int
+do_wait (int fd)
+{
+ char buf;
+ int ret = read (fd, &buf, 1);
+ return ret != 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ char buf[4096];
+ int len;
+ int fd;
+
+ if (argc == 1)
+ return do_server ("/tmp/cairo-sphinx");
+
+ fd = client_socket ("/tmp/cairo-sphinx");
+ if (fd < 0)
+ return 1;
+
+ if (strcmp (argv[1], "client") == 0) {
+ return do_client (fd, argv[2], argv[3], argv[4],
+ CAIRO_CONTENT_COLOR_ALPHA);
+ }
+
+ if (strcmp (argv[1], "wait") == 0) {
+ len = snprintf (buf, sizeof (buf), "wait %s\n", argv[2]);
+ if (! writen (fd, buf, len))
+ return 1;
+
+ return do_wait (fd);
+ }
+
+ if (strcmp (argv[1], "exec") == 0) {
+ len = snprintf (buf, sizeof (buf), "source\n");
+ if (! writen (fd, buf, len))
+ return 1;
+
+ return do_exec (fd, argv+2);
+ }
+
+ if (strcmp (argv[1], "replay") == 0) {
+ len = snprintf (buf, sizeof (buf), "replay %s\n", argv[2]);
+ return ! writen (fd, buf, len);
+ }
+
+ return 0;
+}
diff --git a/util/cairo-trace/.gitignore b/util/cairo-trace/.gitignore
new file mode 100644
index 000000000..b5f866669
--- /dev/null
+++ b/util/cairo-trace/.gitignore
@@ -0,0 +1 @@
+cairo-trace
diff --git a/util/cairo-trace/COPYING b/util/cairo-trace/COPYING
new file mode 100644
index 000000000..37aeee076
--- /dev/null
+++ b/util/cairo-trace/COPYING
@@ -0,0 +1,5 @@
+Cairo is free software.
+
+cairo-trace is released under the terms of the GNU General Public License
+(GPL) version 3. Please see COPYING-GPL-3 for the precise terms and
+conditions.
diff --git a/util/cairo-trace/COPYING-GPL-3 b/util/cairo-trace/COPYING-GPL-3
new file mode 100644
index 000000000..94a9ed024
--- /dev/null
+++ b/util/cairo-trace/COPYING-GPL-3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/util/cairo-trace/Makefile.am b/util/cairo-trace/Makefile.am
new file mode 100644
index 000000000..3278abe42
--- /dev/null
+++ b/util/cairo-trace/Makefile.am
@@ -0,0 +1,40 @@
+cairolibdir = $(libdir)/cairo
+cairooutdir = $(localstatedir)/lib/cairo-trace
+
+bin_SCRIPTS = cairo-trace
+cairolib_LTLIBRARIES = libcairo-trace.la
+
+AM_CPPFLAGS = -I$(top_srcdir)/src \
+ -I$(top_builddir)/src
+
+libcairo_trace_la_SOURCES = trace.c
+libcairo_trace_la_CPPFLAGS = -DCAIRO_TRACE_OUTDIR="\"$(cairooutdir)\"" \
+ $(AM_CPPFLAGS)
+libcairo_trace_la_CFLAGS = $(CAIRO_CFLAGS) $(real_pthread_CFLAGS)
+libcairo_trace_la_LDFLAGS = -no-undefined
+
+libcairo_trace_la_LIBADD = $(real_pthread_LIBS) -lz
+if CAIRO_HAS_DL
+libcairo_trace_la_LIBADD += -ldl
+endif
+
+if CAIRO_HAS_SYMBOL_LOOKUP
+libcairo_trace_la_SOURCES += \
+ lookup-symbol.c \
+ lookup-symbol.h
+libcairo_trace_la_LIBADD += $(BFD_LIBS)
+endif
+
+
+system-install: install
+ -mkdir -p $(cairooutdir)
+ -chmod 01777 $(cairooutdir)
+ grep -sq $(cairolibdir)/libcairo-trace.so /etc/ld.so.preload || echo $(cairolibdir)/libcairo-trace.so >> /etc/ld.so.preload
+
+system-uninstall: uninstall
+ sed -e '/libcairo-trace.so/d' < /etc/ld.so.preload > /tmp/ld.so.preload && mv /tmp/ld.so.preload /etc/ld.so.preload;
+
+EXTRA_DIST = \
+ COPYING \
+ COPYING-GPL-3 \
+ cairo-trace.in
diff --git a/util/cairo-trace/cairo-trace.in b/util/cairo-trace/cairo-trace.in
new file mode 100644
index 000000000..ece90d3eb
--- /dev/null
+++ b/util/cairo-trace/cairo-trace.in
@@ -0,0 +1,136 @@
+#!/bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+nofile=
+flush=
+nocallers=
+nomarkdirty=
+compress=
+
+usage() {
+cat << EOF
+usage: cairo-trace [--no-file] command
+cairo-trace will generate a log of all calls made by command to
+cairo. This log will be stored in a file in the local directory
+called command.pid.trace.
+Whatever else happens is driven by its argument:
+ --flush - Flush the output trace after every call.
+ --no-file - Disable the creation of an output file. Outputs to the
+ terminal instead.
+ --no-callers - Do not lookup the caller address/symbol/line whilst tracing.
+ --mark-dirty - Record image data for cairo_mark_dirty() [default]
+ --no-mark-dirty - Do not record image data for cairo_mark_dirty()
+ --compress - Compress the output with LZMA
+ --profile - Combine --no-callers and --no-mark-dirty and --compress
+
+Environment variables understood by cairo-trace:
+ CAIRO_TRACE_FLUSH - flush the output after every function call.
+ CAIRO_TRACE_LINE_INFO - emit line information for most function calls.
+EOF
+exit
+}
+
+skip=1
+while test $skip -eq 1; do
+ skip=0
+ case $1 in
+ --flush)
+ skip=1
+ flush=1
+ ;;
+ --no-file)
+ skip=1
+ nofile=1
+ ;;
+ --no-callers)
+ skip=1
+ nocallers=1
+ ;;
+ --mark-dirty)
+ skip=1
+ nomarkdirty=
+ ;;
+ --no-mark-dirty)
+ skip=1
+ nomarkdirty=1
+ ;;
+ --compress)
+ skip=1
+ compress=1
+ nofile=1
+ ;;
+ --profile)
+ skip=1
+ compress=1
+ nomarkdirty=1
+ nocallers=1
+ nofile=1
+ ;;
+ --version)
+ echo "cairo-trace, version @CAIRO_VERSION_MAJOR@.@CAIRO_VERSION_MINOR@.@CAIRO_VERSION_MICRO@."
+ exit
+ ;;
+ --help)
+ usage
+ ;;
+ esac
+ if test $skip -eq 1; then
+ shift
+ fi
+done
+
+if test $# -eq 0; then
+ usage
+fi
+
+CAIRO_TRACE_PROG_NAME="$1"
+export CAIRO_TRACE_PROG_NAME
+
+if test "x$CAIRO_TRACE_SO" = "x"; then
+ CAIRO_TRACE_SO=""
+ for lib in @libdir@/cairo/libcairo-trace.@SHLIB_EXT@ @libdir@/cairo/libcairo-trace.@SHLIB_EXT@* @libdir@/cairo/libcairo-trace.*.@SHLIB_EXT@ ; do
+ if test -h "$lib" -o -f "$lib"; then
+ CAIRO_TRACE_SO="$lib"
+ break
+ fi
+ done
+fi
+if test "x$CAIRO_TRACE_SO" = "x"; then
+ echo "Could not find the cairo-trace shared library in @libdir@/cairo/." >&2
+ echo "Set the CAIRO_TRACE_SO environment variable to the full path of the library." >&2
+ exit 15
+fi
+
+LD_PRELOAD="$CAIRO_TRACE_SO"
+DYLD_INSERT_LIBRARIES="$CAIRO_TRACE_SO"
+DYLD_FORCE_FLAT_NAMESPACE=1
+export LD_PRELOAD
+export DYLD_INSERT_LIBRARIES
+export DYLD_FORCE_FLAT_NAMESPACE
+
+if test -n "$nocallers"; then
+ CAIRO_TRACE_LINE_INFO=0
+ export CAIRO_TRACE_LINE_INFO
+fi
+
+if test -n "$nomarkdirty"; then
+ CAIRO_TRACE_MARK_DIRTY=0
+ export CAIRO_TRACE_MARK_DIRTY
+fi
+
+if test -n "$flush"; then
+ CAIRO_TRACE_FLUSH=1
+ export CAIRO_TRACE_FLUSH
+fi
+
+if test -z "$nofile"; then
+ CAIRO_TRACE_OUTDIR=`pwd` "$@"
+elif test -n "$compress"; then
+ name=`basename $1`
+ echo Generating compressed trace file $name.$$.lzma
+ CAIRO_TRACE_FD=3 "$@" 3>&1 >/dev/null | lzma -cz9 > $name.$$.lzma
+else
+ CAIRO_TRACE_FD=3 "$@" 3>&1 >/dev/null
+fi
diff --git a/util/cairo-trace/lookup-symbol.c b/util/cairo-trace/lookup-symbol.c
new file mode 100644
index 000000000..f9665b36f
--- /dev/null
+++ b/util/cairo-trace/lookup-symbol.c
@@ -0,0 +1,331 @@
+/* cairo-trace - a utility to record and replay calls to the Cairo library.
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * A less hacky utility to lookup the debug strings for a particular
+ * .text address.
+ * Derived from backtrace-symbols.c in cairo by Chris Wilson.
+ */
+
+/*
+ A hacky replacement for backtrace_symbols in glibc
+
+ backtrace_symbols in glibc looks up symbols using dladdr which is limited
+ in the symbols that it sees. libbacktracesymbols opens the executable and
+ shared libraries using libbfd and will look up backtrace information using
+ the symbol table and the dwarf line information.
+
+ It may make more sense for this program to use libelf instead of libbfd.
+ However, I have not investigated that yet.
+
+ Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
+
+ Copyright 2007 Jeff Muizelaar
+ */
+
+/* addr2line.c -- convert addresses to line number and function name
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
+
+ This file was part of GNU Binutils.
+ */
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define true 1
+#define false 0
+
+#include "lookup-symbol.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <link.h>
+#include <string.h>
+#include <pthread.h>
+
+#if HAVE_BFD
+#include <bfd.h>
+#include <libiberty.h>
+
+struct symtab {
+ bfd *bfd;
+ asymbol **syms;
+};
+
+struct symbol {
+ int found;
+ bfd_vma pc;
+ struct symtab *symtab;
+ const char *filename;
+ const char *functionname;
+ unsigned int line;
+};
+
+
+static void
+_symtab_fini (struct symtab *symtab)
+{
+ free (symtab->syms);
+ if (symtab->bfd != NULL)
+ bfd_close (symtab->bfd);
+}
+
+/* Read in the symbol table. */
+static int
+_symtab_init (struct symtab *symtab, const char *filename)
+{
+ char **matching;
+ long symcount;
+ unsigned int size;
+
+ symtab->bfd = NULL;
+ symtab->syms = NULL;
+
+ symtab->bfd = bfd_openr (filename, NULL);
+ if (symtab->bfd == NULL)
+ goto BAIL;
+
+ if (bfd_check_format (symtab->bfd, bfd_archive))
+ goto BAIL;
+
+ if (! bfd_check_format_matches (symtab->bfd, bfd_object, &matching))
+ goto BAIL;
+
+ symcount = bfd_read_minisymbols (symtab->bfd, false, (PTR) &symtab->syms, &size);
+ if (symcount == 0) {
+ symcount = bfd_read_minisymbols (symtab->bfd, true /* dynamic */ ,
+ (PTR) &symtab->syms, &size);
+ }
+ if (symcount < 0)
+ goto BAIL;
+
+ if ((bfd_get_file_flags (symtab->bfd) & HAS_SYMS) == 0)
+ goto BAIL;
+
+ return 1;
+
+BAIL:
+ _symtab_fini (symtab);
+ return 0;
+}
+
+/* Look for an address in a section.
+ * This is called via bfd_map_over_sections.
+ */
+static void
+find_address_in_section (bfd *abfd,
+ asection *section,
+ void *data)
+{
+ bfd_vma vma;
+ bfd_size_type size;
+ struct symbol *symbol = data;
+ struct symtab *symtab = symbol->symtab;
+
+ if (symbol->found)
+ return;
+
+ if ((bfd_get_section_flags (symtab->bfd, section) & SEC_ALLOC) == 0)
+ return;
+
+ vma = bfd_get_section_vma (symtab->bfd, section);
+ if (symbol->pc < vma)
+ return;
+
+ size = bfd_section_size (symtab->bfd, section);
+ if (symbol->pc >= vma + size)
+ return;
+
+ symbol->found = bfd_find_nearest_line (symtab->bfd, section,
+ symtab->syms,
+ symbol->pc - vma,
+ &symbol->filename,
+ &symbol->functionname,
+ &symbol->line);
+}
+
+static void
+_symbol_fini (struct symbol *symbol)
+{
+}
+
+static void
+_symbol_init (struct symbol *symbol, struct symtab *symtab, bfd_vma addr)
+{
+ symbol->found = false;
+ symbol->symtab = symtab;
+ symbol->pc = addr;
+}
+
+static void
+_symbol_print (struct symbol *symbol, char *buf, int buflen, const char *filename)
+{
+ const char *name, *h;
+ char path[1024];
+
+ if (! symbol->found)
+ return;
+
+ name = symbol->functionname;
+ if (name == NULL || *name == '\0')
+ name = "??";
+
+ if (symbol->filename != NULL)
+ filename = symbol->filename;
+ if (strcmp (filename, "/proc/self/exe") == 0) {
+ int len = readlink ("/proc/self/exe", path, sizeof (path) - 1);
+ if (len != -1) {
+ path[len] = '\0';
+ filename = path;
+ }
+ }
+ h = strrchr (filename, '/');
+ if (h != NULL)
+ filename = h + 1;
+
+ if (symbol->line) {
+ snprintf (buf, buflen, "%s() [%s:%u]",
+ name, filename, symbol->line);
+ } else {
+ snprintf (buf, buflen, "%s() [%s]", name, filename);
+ }
+}
+#endif
+
+struct file_match {
+ const char *file;
+ ElfW(Addr) address;
+ ElfW(Addr) base;
+ void *hdr;
+};
+
+static int
+find_matching_file (struct dl_phdr_info *info, size_t size, void *data)
+{
+ struct file_match *match = data;
+ /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
+ long n;
+ const ElfW(Phdr) *phdr;
+ ElfW(Addr) load_base = info->dlpi_addr;
+
+ phdr = info->dlpi_phdr;
+ for (n = info->dlpi_phnum; --n >= 0; phdr++) {
+ if (phdr->p_type == PT_LOAD) {
+ ElfW(Addr) vaddr = phdr->p_vaddr + load_base;
+ if (match->address >= vaddr &&
+ match->address < vaddr + phdr->p_memsz)
+ {
+ /* we found a match */
+ match->file = info->dlpi_name;
+ match->base = info->dlpi_addr;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+struct symbol_cache_entry {
+ const void *ptr;
+ struct symbol_cache_entry *hash_prev, *hash_next;
+ char name[0];
+};
+
+static struct symbol_cache_entry *symbol_cache_hash[13477];
+static pthread_mutex_t symbol_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+char *
+lookup_symbol (char *buf, int buflen, const void *ptr)
+{
+ struct file_match match;
+#if HAVE_BFD
+ struct symtab symtab;
+ struct symbol symbol;
+#endif
+ struct symbol_cache_entry *cache;
+ int bucket;
+ int len;
+
+ bucket = (unsigned long) ptr % (sizeof (symbol_cache_hash) / sizeof (symbol_cache_hash[0]));
+ pthread_mutex_lock (&symbol_cache_mutex);
+ for (cache = symbol_cache_hash[bucket];
+ cache != NULL;
+ cache = cache->hash_next)
+ {
+ if (cache->ptr == ptr) {
+ if (cache->hash_prev != NULL) {
+ cache->hash_prev->hash_next = cache->hash_next;
+ if (cache->hash_next != NULL)
+ cache->hash_next->hash_prev = cache->hash_prev;
+ cache->hash_prev = NULL;
+ cache->hash_next = symbol_cache_hash[bucket];
+ symbol_cache_hash[bucket]->hash_prev = cache;
+ symbol_cache_hash[bucket] = cache;
+ }
+
+ pthread_mutex_unlock (&symbol_cache_mutex);
+ return cache->name;
+ }
+ }
+ pthread_mutex_unlock (&symbol_cache_mutex);
+
+ match.file = NULL;
+ match.address = (ElfW(Addr)) ptr;
+ dl_iterate_phdr (find_matching_file, &match);
+
+ snprintf (buf, buflen, "0x%llx",
+ (long long unsigned int) match.address);
+
+ if (match.file == NULL || *match.file == '\0')
+ match.file = "/proc/self/exe";
+
+#if HAVE_BFD
+ if (_symtab_init (&symtab, match.file)) {
+ _symbol_init (&symbol, &symtab, match.address - match.base);
+ bfd_map_over_sections (symtab.bfd, find_address_in_section, &symbol);
+ if (symbol.found)
+ _symbol_print (&symbol, buf, buflen, match.file);
+ _symbol_fini (&symbol);
+
+ _symtab_fini (&symtab);
+ }
+#endif
+
+ len = strlen (buf);
+ cache = malloc (sizeof (struct symbol_cache_entry) + len + 1);
+ if (cache != NULL) {
+ cache->ptr = ptr;
+ memcpy (cache->name, buf, len + 1);
+
+ pthread_mutex_lock (&symbol_cache_mutex);
+ cache->hash_prev = NULL;
+ cache->hash_next = symbol_cache_hash[bucket];
+ if (symbol_cache_hash[bucket] != NULL)
+ symbol_cache_hash[bucket]->hash_prev = cache;
+ symbol_cache_hash[bucket] = cache;
+ pthread_mutex_unlock (&symbol_cache_mutex);
+ }
+
+ return buf;
+}
diff --git a/util/cairo-trace/lookup-symbol.h b/util/cairo-trace/lookup-symbol.h
new file mode 100644
index 000000000..83817edc6
--- /dev/null
+++ b/util/cairo-trace/lookup-symbol.h
@@ -0,0 +1,24 @@
+/* cairo-trace - a utility to record and replay calls to the Cairo library.
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LOOKUP_SYMBOLS_H
+
+char *
+lookup_symbol (char *buf, int len, const void *ptr);
+
+#endif /*LOOKUP_SYMBOLS_H */
diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
new file mode 100644
index 000000000..d5d76689c
--- /dev/null
+++ b/util/cairo-trace/trace.c
@@ -0,0 +1,5581 @@
+/* cairo-trace - a utility to record and replay calls to the Cairo library.
+ *
+ * Copyright © 2008 Chris Wilson
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* The autoconf on OpenBSD 4.5 produces the malformed constant name
+ * SIZEOF_VOID__ rather than SIZEOF_VOID_P. Work around that here. */
+#if !defined(SIZEOF_VOID_P) && defined(SIZEOF_VOID__)
+# define SIZEOF_VOID_P SIZEOF_VOID__
+#endif
+
+#include <dlfcn.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <zlib.h>
+#include <math.h>
+#include <locale.h> /* for locale independent %f printing */
+#include <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdarg.h>
+
+#include <cairo.h>
+#if CAIRO_HAS_FT_FONT
+# include <cairo-ft.h>
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+#ifndef CAIRO_TRACE_OUTDIR
+#define CAIRO_TRACE_OUTDIR "."
+#endif
+
+#define DEBUG_STACK 0
+
+#if HAVE_BYTESWAP_H
+# include <byteswap.h>
+#endif
+#ifndef bswap_16
+# define bswap_16(p) \
+ (((((uint16_t)(p)) & 0x00ff) << 8) | \
+ (((uint16_t)(p)) >> 8))
+#endif
+#ifndef bswap_32
+# define bswap_32(p) \
+ (((((uint32_t)(p)) & 0x000000ff) << 24) | \
+ ((((uint32_t)(p)) & 0x0000ff00) << 8) | \
+ ((((uint32_t)(p)) & 0x00ff0000) >> 8) | \
+ ((((uint32_t)(p))) >> 24))
+#endif
+
+#if WORDS_BIGENDIAN
+#define le16(x) bswap_16 (x)
+#define le32(x) bswap_32 (x)
+#define be16(x) x
+#define be32(x) x
+#define to_be32(x) x
+#else
+#define le16(x) x
+#define le32(x) x
+#define be16(x) bswap_16 (x)
+#define be32(x) bswap_32 (x)
+#define to_be32(x) bswap_32 (x)
+#endif
+
+#if CAIRO_HAS_SYMBOL_LOOKUP
+#include "lookup-symbol.h"
+#endif
+
+/* Reverse the bits in a byte with 7 operations (no 64-bit):
+ * Devised by Sean Anderson, July 13, 2001.
+ * Source: http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
+ */
+#define CAIRO_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16)
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+#define CAIRO_PRINTF_FORMAT(fmt_index, va_index) \
+ __attribute__((__format__(__printf__, fmt_index, va_index)))
+#else
+#define CAIRO_PRINTF_FORMAT(fmt_index, va_index)
+#endif
+
+/* XXX implement manual vprintf so that the user can control precision of
+ * printed numbers.
+ */
+
+static void *_dlhandle = RTLD_NEXT;
+#define DLCALL(name, args...) ({ \
+ static typeof (&name) name##_real; \
+ if (name##_real == NULL) { \
+ name##_real = (typeof (&name))(dlsym (_dlhandle, #name)); \
+ if (name##_real == NULL && _dlhandle == RTLD_NEXT) { \
+ _dlhandle = dlopen ("libcairo." SHARED_LIB_EXT, RTLD_LAZY); \
+ name##_real = (typeof (&name))(dlsym (_dlhandle, #name)); \
+ assert (name##_real != NULL); \
+ } \
+ } \
+ (*name##_real) (args); \
+})
+
+#ifndef ARRAY_LENGTH
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+#endif
+
+#if SIZEOF_VOID_P == 4
+#define PTR_SHIFT 2
+#elif SIZEOF_VOID_P == 8
+#define PTR_SHIFT 3
+#else
+#error Unexpected pointer size
+#endif
+#define BUCKET(b, ptr) (((unsigned long) (ptr) >> PTR_SHIFT) % ARRAY_LENGTH (b))
+
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#define _BOOLEAN_EXPR(expr) \
+ __extension__ ({ \
+ int _boolean_var_; \
+ if (expr) \
+ _boolean_var_ = 1; \
+ else \
+ _boolean_var_ = 0; \
+ _boolean_var_; \
+})
+#define LIKELY(expr) (__builtin_expect (_BOOLEAN_EXPR(expr), 1))
+#define UNLIKELY(expr) (__builtin_expect (_BOOLEAN_EXPR(expr), 0))
+#else
+#define LIKELY(expr) (expr)
+#define UNLIKELY(expr) (expr)
+#endif
+
+typedef struct _object Object;
+typedef struct _type Type;
+
+struct _object {
+ const void *addr;
+ Type *type;
+ unsigned long int token;
+ int width, height;
+ cairo_bool_t foreign;
+ cairo_bool_t defined;
+ cairo_bool_t unknown;
+ int operand;
+ void *data;
+ void (*destroy)(void *);
+ Object *next, *prev;
+};
+
+struct _type {
+ const char *name;
+
+ enum operand_type {
+ NONE,
+ SURFACE,
+ CONTEXT,
+ FONT_FACE,
+ PATTERN,
+ SCALED_FONT,
+ _N_OP_TYPES
+ } op_type;
+ const char *op_code;
+
+ pthread_mutex_t mutex;
+ struct _bitmap {
+ unsigned long int min;
+ unsigned long int count;
+ unsigned int map[64];
+ struct _bitmap *next;
+ } map;
+ Object *objects[607];
+ Type *next;
+};
+
+static struct _type_table {
+ pthread_mutex_t mutex;
+ Type *op_types[_N_OP_TYPES];
+} Types;
+
+static FILE *logfile;
+static cairo_bool_t _flush;
+static cairo_bool_t _error;
+static cairo_bool_t _line_info;
+static cairo_bool_t _mark_dirty;
+static const cairo_user_data_key_t destroy_key;
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static pthread_key_t counter_key;
+
+static void _init_trace (void);
+
+#define INIT_TRACE_ONCE() pthread_once (&once_control, _init_trace)
+
+#if __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun)
+# define _enter_trace() INIT_TRACE_ONCE ()
+# define _exit_trace() do { } while (0)
+# define _should_trace() 1
+# define USE_ENTER_EXIT 0
+#else
+static void _enter_trace (void);
+static void _exit_trace (void);
+static cairo_bool_t _should_trace (void);
+# define USE_ENTER_EXIT 1
+#endif
+
+#if HAVE_BUILTIN_RETURN_ADDRESS && CAIRO_HAS_SYMBOL_LOOKUP
+#define _emit_line_info() do { \
+ if (_line_info && _write_lock ()) { \
+ void *addr = __builtin_return_address(0); \
+ char caller[1024]; \
+ _trace_printf ("%% %s() called by %s\n", __FUNCTION__, \
+ lookup_symbol (caller, sizeof (caller), addr)); \
+ _write_unlock (); \
+ } \
+} while (0)
+#else
+#define _emit_line_info()
+#endif
+
+static void
+_type_release_token (Type *t, unsigned long int token)
+{
+ struct _bitmap *b, **prev = NULL;
+
+ b = &t->map;
+ while (b != NULL) {
+ if (token < b->min + sizeof (b->map) * CHAR_BIT) {
+ unsigned int bit, elem;
+
+ token -= b->min;
+ elem = token / (sizeof (b->map[0]) * CHAR_BIT);
+ bit = token % (sizeof (b->map[0]) * CHAR_BIT);
+ b->map[elem] &= ~(1 << bit);
+ if (! --b->count && prev) {
+ *prev = b->next;
+ free (b);
+ }
+ return;
+ }
+ prev = &b->next;
+ b = b->next;
+ }
+}
+
+static unsigned long int
+_type_next_token (Type *t)
+{
+ struct _bitmap *b, *bb, **prev = NULL;
+ unsigned long int min = 0;
+
+ b = &t->map;
+ while (b != NULL) {
+ if (b->min != min)
+ break;
+
+ if (b->count < sizeof (b->map) * CHAR_BIT) {
+ unsigned int n, m, bit;
+ for (n = 0; n < ARRAY_LENGTH (b->map); n++) {
+ if (b->map[n] == (unsigned int) -1)
+ continue;
+
+ for (m=0, bit=1; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) {
+ if ((b->map[n] & bit) == 0) {
+ b->map[n] |= bit;
+ b->count++;
+ return n * sizeof (b->map[0])*CHAR_BIT + m + b->min;
+ }
+ }
+ }
+ }
+ min += sizeof (b->map) * CHAR_BIT;
+
+ prev = &b->next;
+ b = b->next;
+ }
+
+ bb = malloc (sizeof (struct _bitmap));
+ *prev = bb;
+ bb->next = b;
+ bb->min = min;
+ bb->count = 1;
+ bb->map[0] = 0x1;
+ memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0]));
+
+ return min;
+}
+
+static void
+_object_destroy (Object *obj)
+{
+ int bucket;
+
+ pthread_mutex_lock (&obj->type->mutex);
+ bucket = BUCKET (obj->type->objects, obj->addr);
+ _type_release_token (obj->type, obj->token);
+
+ if (obj->prev != NULL)
+ obj->prev->next = obj->next;
+ else
+ obj->type->objects[bucket] = obj->next;
+
+ if (obj->next != NULL)
+ obj->next->prev = obj->prev;
+ pthread_mutex_unlock (&obj->type->mutex);
+
+ if (obj->data != NULL && obj->destroy != NULL)
+ obj->destroy (obj->data);
+
+ free (obj);
+}
+
+static void
+_type_create (const char *typename,
+ enum operand_type op_type,
+ const char *op_code)
+{
+ Type *t;
+
+ pthread_mutex_lock (&Types.mutex);
+
+ t = malloc (sizeof (Type));
+ t->name = typename;
+ t->op_type = op_type;
+ t->op_code = op_code;
+
+ pthread_mutex_init (&t->mutex, NULL);
+
+ t->map.min = 0;
+ t->map.count = 0;
+ memset (t->map.map, 0, sizeof (t->map.map));
+ t->map.next = NULL;
+
+ memset (t->objects, 0, sizeof (t->objects));
+
+ t->next = NULL;
+
+ Types.op_types[op_type] = t;
+ pthread_mutex_unlock (&Types.mutex);
+}
+
+static Type *
+_get_type (enum operand_type type)
+{
+ return Types.op_types[type];
+}
+
+static void
+_type_destroy (Type *t)
+{
+ int n;
+ struct _bitmap *b;
+
+ for (n = 0; n < ARRAY_LENGTH (t->objects); n++) {
+ Object *obj = t->objects[n];
+ while (obj != NULL) {
+ Object *next = obj->next;
+ _object_destroy (obj);
+ obj = next;
+ }
+ }
+
+ b = t->map.next;
+ while (b != NULL) {
+ struct _bitmap *next = b->next;
+ free (b);
+ b = next;
+ }
+
+ pthread_mutex_destroy (&t->mutex);
+ free (t);
+}
+
+static Object *
+_type_get_object (Type *type, const void *ptr)
+{
+ Object *obj;
+ int bucket = BUCKET (type->objects, ptr);
+
+ for (obj = type->objects[bucket]; obj != NULL; obj = obj->next) {
+ if (obj->addr == ptr) {
+ if (obj->prev != NULL) { /* mru */
+ obj->prev->next = obj->next;
+ if (obj->next != NULL)
+ obj->next->prev = obj->prev;
+ obj->prev = NULL;
+ type->objects[bucket]->prev = obj;
+ obj->next = type->objects[bucket];
+ type->objects[bucket] = obj;
+ }
+ return obj;
+ }
+ }
+
+ return NULL;
+}
+
+static Object *
+_object_create (Type *type, const void *ptr)
+{
+ Object *obj;
+ int bucket = BUCKET (type->objects, ptr);
+
+ obj = malloc (sizeof (Object));
+ obj->unknown = TRUE;
+ obj->defined = FALSE;
+ obj->foreign = FALSE;
+ obj->operand = -1;
+ obj->type = type;
+ obj->addr = ptr;
+ obj->token = _type_next_token (type);
+ obj->data = NULL;
+ obj->destroy = NULL;
+ obj->prev = NULL;
+ obj->next = type->objects[bucket];
+ if (type->objects[bucket] != NULL)
+ type->objects[bucket]->prev = obj;
+ type->objects[bucket] = obj;
+
+ return obj;
+}
+
+#if USE_ENTER_EXIT
+static int *
+_get_counter (void)
+{
+ int *counter = pthread_getspecific (counter_key);
+ if (counter == NULL) {
+ counter = calloc(1, sizeof(int));
+ pthread_setspecific (counter_key, counter);
+ }
+ return counter;
+}
+
+static void
+_enter_trace (void)
+{
+ INIT_TRACE_ONCE ();
+ _get_counter ()[0]++;
+}
+
+static void
+_exit_trace (void)
+{
+ _get_counter ()[0]--;
+}
+
+static cairo_bool_t
+_should_trace (void)
+{
+ return _get_counter ()[0] <= 1;
+}
+#endif /* USE_ENTER_EXIT */
+
+static void
+_init_trace (void)
+{
+ pthread_mutex_init (&Types.mutex, NULL);
+ pthread_key_create (&counter_key, free);
+
+ _type_create ("unclassed", NONE, "");
+ _type_create ("cairo_t", CONTEXT, "c");
+ _type_create ("cairo_font_face_t", FONT_FACE, "f");
+ _type_create ("cairo_pattern_t", PATTERN, "p");
+ _type_create ("cairo_scaled_font_t", SCALED_FONT, "sf");
+ _type_create ("cairo_surface_t", SURFACE, "s");
+}
+
+static void
+_close_trace (void)
+{
+ if (logfile != NULL) {
+ fclose (logfile);
+ logfile = NULL;
+ }
+}
+
+static void __attribute__ ((destructor))
+_fini_trace (void)
+{
+ int n;
+
+ _close_trace ();
+
+ for (n = 0; n < ARRAY_LENGTH (Types.op_types); n++) {
+ if (Types.op_types[n]) {
+ _type_destroy (Types.op_types[n]);
+ Types.op_types[n] = NULL;
+ }
+ }
+
+ pthread_mutex_destroy (&Types.mutex);
+}
+
+/* Format a double in a locale independent way and trim trailing
+ * zeros. Based on code from Alex Larson <alexl@redhat.com>.
+ * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html
+ *
+ * The code in the patch is copyright Red Hat, Inc under the LGPL.
+ */
+#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6
+static void
+_trace_dtostr (char *buffer, size_t size, double d)
+{
+ struct lconv *locale_data;
+ const char *decimal_point;
+ int decimal_point_len;
+ char *p;
+ int decimal_len;
+ int num_zeros, decimal_digits;
+
+ /* Omit the minus sign from negative zero. */
+ if (d == 0.0)
+ d = 0.0;
+
+ locale_data = localeconv ();
+ decimal_point = locale_data->decimal_point;
+ decimal_point_len = strlen (decimal_point);
+
+ /* Using "%f" to print numbers less than 0.1 will result in
+ * reduced precision due to the default 6 digits after the
+ * decimal point.
+ *
+ * For numbers is < 0.1, we print with maximum precision and count
+ * the number of zeros between the decimal point and the first
+ * significant digit. We then print the number again with the
+ * number of decimal places that gives us the required number of
+ * significant digits. This ensures the number is correctly
+ * rounded.
+ */
+ if (fabs (d) >= 0.1) {
+ snprintf (buffer, size, "%f", d);
+ } else {
+ snprintf (buffer, size, "%.18f", d);
+ p = buffer;
+
+ if (*p == '+' || *p == '-')
+ p++;
+
+ while (isdigit (*p))
+ p++;
+
+ if (strncmp (p, decimal_point, decimal_point_len) == 0)
+ p += decimal_point_len;
+
+ num_zeros = 0;
+ while (*p++ == '0')
+ num_zeros++;
+
+ decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL;
+
+ if (decimal_digits < 18)
+ snprintf (buffer, size, "%.*f", decimal_digits, d);
+ }
+ p = buffer;
+
+ if (*p == '+' || *p == '-')
+ p++;
+
+ while (isdigit (*p))
+ p++;
+
+ if (strncmp (p, decimal_point, decimal_point_len) == 0) {
+ *p = '.';
+ decimal_len = strlen (p + decimal_point_len);
+ memmove (p + 1, p + decimal_point_len, decimal_len);
+ p[1 + decimal_len] = 0;
+
+ /* Remove trailing zeros and decimal point if possible. */
+ for (p = p + decimal_len; *p == '0'; p--)
+ *p = 0;
+
+ if (*p == '.') {
+ *p = 0;
+ p--;
+ }
+ }
+}
+
+enum {
+ LENGTH_MODIFIER_LONG = 0x100
+};
+
+/* Here's a limited reimplementation of printf. The reason for doing
+ * this is primarily to special case handling of doubles. We want
+ * locale independent formatting of doubles and we want to trim
+ * trailing zeros. This is handled by dtostr() above, and the code
+ * below handles everything else by calling snprintf() to do the
+ * formatting. This functionality is only for internal use and we
+ * only implement the formats we actually use.
+ */
+static void CAIRO_PRINTF_FORMAT(1, 0)
+_trace_vprintf (const char *fmt, va_list ap)
+{
+#define SINGLE_FMT_BUFFER_SIZE 32
+ char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE];
+ int single_fmt_length;
+ char *p;
+ const char *f, *start;
+ int length_modifier, width;
+ cairo_bool_t var_width;
+ int ret_ignored;
+
+ assert (_should_trace ());
+
+ f = fmt;
+ p = buffer;
+ while (*f != '\0') {
+ if (*f != '%') {
+ *p++ = *f++;
+ continue;
+ }
+
+ start = f;
+ f++;
+
+ if (*f == '0')
+ f++;
+
+ var_width = 0;
+ if (*f == '*') {
+ var_width = 1;
+ f++;
+ }
+
+ while (isdigit (*f))
+ f++;
+
+ length_modifier = 0;
+ if (*f == 'l') {
+ length_modifier = LENGTH_MODIFIER_LONG;
+ f++;
+ }
+
+ /* The only format strings exist in the cairo implementation
+ * itself. So there's an internal consistency problem if any
+ * of them is larger than our format buffer size. */
+ single_fmt_length = f - start + 1;
+
+ /* Reuse the format string for this conversion. */
+ memcpy (single_fmt, start, single_fmt_length);
+ single_fmt[single_fmt_length] = '\0';
+
+ /* Flush contents of buffer before snprintf()'ing into it. */
+ ret_ignored = fwrite (buffer, 1, p-buffer, logfile);
+
+ /* We group signed and unsigned together in this switch, the
+ * only thing that matters here is the size of the arguments,
+ * since we're just passing the data through to sprintf(). */
+ switch (*f | length_modifier) {
+ case '%':
+ buffer[0] = *f;
+ buffer[1] = 0;
+ break;
+ case 'd':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ if (var_width) {
+ width = va_arg (ap, int);
+ snprintf (buffer, sizeof buffer,
+ single_fmt, width, va_arg (ap, int));
+ } else {
+ snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int));
+ }
+ break;
+ case 'd' | LENGTH_MODIFIER_LONG:
+ case 'u' | LENGTH_MODIFIER_LONG:
+ case 'o' | LENGTH_MODIFIER_LONG:
+ case 'x' | LENGTH_MODIFIER_LONG:
+ case 'X' | LENGTH_MODIFIER_LONG:
+ if (var_width) {
+ width = va_arg (ap, int);
+ snprintf (buffer, sizeof buffer,
+ single_fmt, width, va_arg (ap, long int));
+ } else {
+ snprintf (buffer, sizeof buffer,
+ single_fmt, va_arg (ap, long int));
+ }
+ break;
+ case 's':
+ snprintf (buffer, sizeof buffer,
+ single_fmt, va_arg (ap, const char *));
+ break;
+ case 'f':
+ case 'g':
+ _trace_dtostr (buffer, sizeof buffer, va_arg (ap, double));
+ break;
+ case 'c':
+ buffer[0] = va_arg (ap, int);
+ buffer[1] = 0;
+ break;
+ default:
+ break;
+ }
+ p = buffer + strlen (buffer);
+ f++;
+ }
+
+ ret_ignored = fwrite (buffer, 1, p-buffer, logfile);
+ (void)ret_ignored;
+}
+
+static void CAIRO_PRINTF_FORMAT(1, 2)
+_trace_printf (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ _trace_vprintf (fmt, ap);
+ va_end (ap);
+}
+
+static void
+get_prog_name (char *buf, int length)
+{
+ char *slash;
+ FILE *file;
+
+ memset (buf, 0, length);
+ if (length == 0)
+ return;
+
+ file = fopen ("/proc/self/cmdline", "rb");
+ if (file != NULL) {
+ slash = fgets (buf, length, file);
+ fclose (file);
+
+ if (slash == NULL)
+ return;
+ } else {
+ char const *name = getenv ("CAIRO_TRACE_PROG_NAME");
+ if (name != NULL) {
+ strncpy (buf, name, length-1);
+ }
+ }
+
+ slash = strrchr (buf, '/');
+ if (slash != NULL) {
+ size_t len = strlen (slash+1);
+ memmove (buf, slash+1, len+1);
+ }
+}
+
+static void
+_emit_header (void)
+{
+ char name[4096] = "";
+
+ get_prog_name (name, sizeof (name));
+
+ _trace_printf ("%%!CairoScript - %s\n", name);
+}
+
+static cairo_bool_t
+_init_logfile (void)
+{
+ static cairo_bool_t initialized;
+ char buf[4096];
+ const char *filename;
+ const char *env;
+
+ if (initialized)
+ return logfile != NULL;
+
+ initialized = TRUE;
+
+ env = getenv ("CAIRO_TRACE_FLUSH");
+ if (env != NULL)
+ _flush = atoi (env);
+
+ _line_info = TRUE;
+ env = getenv ("CAIRO_TRACE_LINE_INFO");
+ if (env != NULL)
+ _line_info = atoi (env);
+
+ _mark_dirty = TRUE;
+ env = getenv ("CAIRO_TRACE_MARK_DIRTY");
+ if (env != NULL)
+ _mark_dirty = atoi (env);
+
+ filename = getenv ("CAIRO_TRACE_FD");
+ if (filename != NULL) {
+ int fd = atoi (filename);
+ if (fd == -1)
+ return FALSE;
+
+ logfile = fdopen (fd, "w");
+ if (logfile == NULL) {
+ fprintf (stderr, "Failed to open trace file descriptor '%s': %s\n",
+ filename, strerror (errno));
+ return FALSE;
+ }
+
+ setenv ("CAIRO_TRACE_FD", "-1", 1);
+ goto done;
+ }
+
+ filename = getenv ("CAIRO_TRACE_OUTFILE_EXACT");
+ if (filename == NULL) {
+ char name[4096] = "";
+
+ filename = getenv ("CAIRO_TRACE_OUTDIR");
+ if (filename == NULL)
+ filename = CAIRO_TRACE_OUTDIR;
+
+ get_prog_name (name, sizeof (name));
+ if (*name == '\0')
+ strcpy (name, "cairo-trace.dat");
+
+ snprintf (buf, sizeof (buf), "%s/%s.%d.trace",
+ filename, name, getpid());
+
+ filename = buf;
+ } else {
+ setenv ("CAIRO_TRACE_FD", "-1", 1);
+ }
+
+ logfile = fopen (filename, "wb");
+ if (logfile == NULL) {
+ fprintf (stderr, "Failed to open trace file '%s': %s\n",
+ filename, strerror (errno));
+ return FALSE;
+ }
+
+ fprintf (stderr, "cairo-trace: Recording cairo trace data to %s\n",
+ filename);
+
+done:
+ atexit (_close_trace);
+ _emit_header ();
+ return TRUE;
+}
+
+static cairo_bool_t
+_write_lock (void)
+{
+ if (_error)
+ return FALSE;
+
+ if (! _should_trace ())
+ return FALSE;
+
+ if (! _init_logfile ())
+ return FALSE;
+
+#if HAVE_FLOCKFILE && HAVE_FUNLOCKFILE
+ flockfile (logfile);
+#endif
+ return TRUE;
+}
+
+static void
+_write_unlock (void)
+{
+ if (logfile == NULL)
+ return;
+
+#if HAVE_FLOCKFILE && HAVE_FUNLOCKFILE
+ funlockfile (logfile);
+#endif
+
+ if (_flush)
+ fflush (logfile);
+}
+
+
+static Object *
+_type_object_create (enum operand_type op_type, const void *ptr)
+{
+ Type *type;
+ Object *obj;
+
+ type = _get_type (op_type);
+
+ pthread_mutex_lock (&type->mutex);
+ obj = _object_create (type, ptr);
+ pthread_mutex_unlock (&type->mutex);
+
+ return obj;
+}
+
+static Object *
+_get_object (enum operand_type op_type, const void *ptr)
+{
+ Type *type;
+ Object *obj;
+
+ type = _get_type (op_type);
+ pthread_mutex_lock (&type->mutex);
+ obj = _type_get_object (type, ptr);
+ pthread_mutex_unlock (&type->mutex);
+
+ return obj;
+}
+
+static Object *current_object[2048]; /* XXX limit operand stack */
+static int current_stack_depth;
+
+static void
+dump_stack(const char *func)
+{
+#if DEBUG_STACK
+ int n;
+
+ _trace_printf ("%% %s: stack[%d] = [", func, current_stack_depth);
+ fflush (logfile);
+ for (n = 0; n < current_stack_depth; n++) {
+ Object *obj = current_object[n];
+ assert(obj && obj->type);
+ _trace_printf (" %s%s%ld",
+ obj->defined ? "" : "*",
+ obj->type->op_code, obj->token);
+ fflush (logfile);
+ }
+ _trace_printf (" ]\n");
+ fflush (logfile);
+#endif
+}
+
+static void
+ensure_operands (int num_operands)
+{
+ if (current_stack_depth < num_operands) {
+ int n;
+
+ fprintf (stderr, "Operand stack underflow!\n");
+ for (n = 0; n < current_stack_depth; n++) {
+ Object *obj = current_object[n];
+
+ fprintf (stderr, " [%3d] = %s%ld\n",
+ n, obj->type->op_code, obj->token);
+ }
+
+ abort ();
+ }
+}
+
+static void
+_consume_operand (bool discard)
+{
+ Object *obj;
+
+ ensure_operands (1);
+ obj = current_object[--current_stack_depth];
+ if (!discard && ! obj->defined) {
+ _trace_printf ("dup /%s%ld exch def\n",
+ obj->type->op_code,
+ obj->token);
+ obj->defined = TRUE;
+ }
+ obj->operand = -1;
+}
+
+static void
+_exch_operands (void)
+{
+ Object *tmp;
+
+ ensure_operands (2);
+ tmp = current_object[current_stack_depth-1];
+ tmp->operand--;
+ current_object[current_stack_depth-1] = current_object[current_stack_depth-2];
+ current_object[current_stack_depth-2] = tmp;
+ tmp = current_object[current_stack_depth-1];
+ tmp->operand++;
+}
+
+static cairo_bool_t
+_pop_operands_to_depth (int depth)
+{
+ if (depth < 0)
+ return FALSE;
+
+ assert(current_stack_depth >= depth);
+ if (current_stack_depth == depth)
+ return TRUE;
+
+ while (current_stack_depth > depth + 1) {
+ Object *c_obj;
+
+ ensure_operands (1);
+ c_obj = current_object[--current_stack_depth];
+
+ assert(c_obj);
+ assert(c_obj->type);
+
+ if (! c_obj->defined) {
+ current_stack_depth++;
+ return FALSE;
+ }
+
+ _trace_printf ("pop %% %s%ld\n",
+ c_obj->type->op_code, c_obj->token);
+ c_obj->operand = -1;
+ }
+
+ _exch_operands ();
+ _trace_printf ("exch\n");
+
+ dump_stack(__func__);
+ return TRUE;
+}
+
+static cairo_bool_t
+_pop_operands_to_object (Object *obj)
+{
+ if (!obj)
+ return FALSE;
+
+ if (obj->operand == -1)
+ return FALSE;
+
+ if (obj->operand == current_stack_depth - 1)
+ return TRUE;
+
+ if (obj->operand == current_stack_depth - 2) {
+ _exch_operands ();
+ _trace_printf ("exch ");
+ return TRUE;
+ }
+
+ return _pop_operands_to_depth (obj->operand + 1);
+}
+
+static cairo_bool_t
+_pop_operands_to (enum operand_type t, const void *ptr)
+{
+ return _pop_operands_to_object (_get_object (t, ptr));
+}
+
+static cairo_bool_t
+_is_current_object (Object *obj, int depth)
+{
+ if (current_stack_depth <= depth)
+ return FALSE;
+ return current_object[current_stack_depth-depth-1] == obj;
+}
+
+static cairo_bool_t
+_is_current (enum operand_type type, const void *ptr, int depth)
+{
+ return _is_current_object (_get_object (type, ptr), depth);
+}
+
+static void
+_push_object(Object *obj)
+{
+ assert(obj->operand == -1);
+
+ if (current_stack_depth == ARRAY_LENGTH (current_object)) {
+ int n;
+
+ fprintf (stderr, "Operand stack overflow!\n");
+ for (n = 0; n < current_stack_depth; n++) {
+ obj = current_object[n];
+
+ fprintf (stderr, " [%3d] = %s%ld\n",
+ n, obj->type->op_code, obj->token);
+ }
+
+ abort ();
+ }
+
+ obj->operand = current_stack_depth;
+ current_object[current_stack_depth++] = obj;
+}
+
+static void
+_push_operand (enum operand_type t, const void *ptr)
+{
+ _push_object(_get_object(t, ptr));
+}
+
+static void
+_object_remove (Object *obj)
+{
+ if (obj->operand != -1) {
+ ensure_operands (1);
+ if (obj->operand == current_stack_depth - 1) {
+ _trace_printf ("pop %% %s%ld destroyed\n",
+ obj->type->op_code, obj->token);
+ } else if (obj->operand == current_stack_depth - 2) {
+ _exch_operands ();
+ _trace_printf ("exch pop %% %s%ld destroyed\n",
+ obj->type->op_code, obj->token);
+ } else {
+ int n;
+
+ _trace_printf ("%d -1 roll pop %% %s%ld destroyed\n",
+ current_stack_depth - obj->operand,
+ obj->type->op_code, obj->token);
+
+ for (n = obj->operand; n < current_stack_depth - 1; n++) {
+ current_object[n] = current_object[n+1];
+ current_object[n]->operand = n;
+ }
+ }
+ obj->operand = -1;
+
+ current_stack_depth--;
+ dump_stack(__func__);
+ }
+}
+
+static void
+_object_undef (void *ptr)
+{
+ Object *obj = ptr;
+
+ if (_write_lock ()) {
+ _object_remove (obj);
+
+ if (obj->defined) {
+ _trace_printf ("/%s%ld undef\n",
+ obj->type->op_code, obj->token);
+ }
+
+ _write_unlock ();
+ }
+
+ _object_destroy (obj);
+}
+
+static long
+_create_context_id (cairo_t *cr)
+{
+ Object *obj;
+
+ obj = _get_object (CONTEXT, cr);
+ if (obj == NULL) {
+ obj = _type_object_create (CONTEXT, cr);
+ DLCALL (cairo_set_user_data,
+ cr, &destroy_key, obj, _object_undef);
+ }
+
+ return obj->token;
+}
+
+static long
+_get_id (enum operand_type op_type, const void *ptr)
+{
+ Object *obj;
+
+ obj = _get_object (op_type, ptr);
+ if (obj == NULL) {
+ if (logfile != NULL) {
+ _trace_printf ("%% Unknown object of type %s, trace is incomplete.",
+ _get_type (op_type)->name);
+ }
+ _error = TRUE;
+ return -1;
+ }
+
+ return obj->token;
+}
+
+static cairo_bool_t
+_has_id (enum operand_type op_type, const void *ptr)
+{
+ return _get_object (op_type, ptr) != NULL;
+}
+
+static long
+_create_font_face_id (cairo_font_face_t *font_face)
+{
+ Object *obj;
+
+ obj = _get_object (FONT_FACE, font_face);
+ if (obj == NULL) {
+ obj = _type_object_create (FONT_FACE, font_face);
+ DLCALL (cairo_font_face_set_user_data,
+ font_face, &destroy_key, obj, _object_undef);
+ }
+
+ return obj->token;
+}
+
+static long
+_get_font_face_id (cairo_font_face_t *font_face)
+{
+ return _get_id (FONT_FACE, font_face);
+}
+
+static void
+_emit_font_face_id (cairo_font_face_t *font_face)
+{
+ Object *obj = _get_object (FONT_FACE, font_face);
+ if (obj == NULL) {
+ _trace_printf ("null ");
+ } else {
+ if (obj->defined) {
+ _trace_printf ("f%ld ", obj->token);
+ } else {
+ _trace_printf ("%d index ", current_stack_depth - obj->operand - 1);
+ }
+ }
+}
+
+static cairo_bool_t
+_has_pattern_id (cairo_pattern_t *pattern)
+{
+ return _has_id (PATTERN, pattern);
+}
+
+static long
+_create_pattern_id (cairo_pattern_t *pattern)
+{
+ Object *obj;
+
+ obj = _get_object (PATTERN, pattern);
+ if (obj == NULL) {
+ obj = _type_object_create (PATTERN, pattern);
+ DLCALL (cairo_pattern_set_user_data,
+ pattern, &destroy_key, obj, _object_undef);
+ }
+
+ return obj->token;
+}
+
+static void
+_emit_pattern_id (cairo_pattern_t *pattern)
+{
+ Object *obj = _get_object (PATTERN, pattern);
+ if (obj == NULL) {
+ _trace_printf ("null ");
+ } else {
+ if (obj->defined) {
+ _trace_printf ("p%ld ", obj->token);
+ } else {
+ _trace_printf ("%d index ",
+ current_stack_depth - obj->operand - 1);
+ }
+ }
+}
+
+static void
+_emit_scaled_font_id (const cairo_scaled_font_t *scaled_font)
+{
+ Object *obj = _get_object (SCALED_FONT, scaled_font);
+ if (obj == NULL) {
+ _trace_printf ("null ");
+ } else {
+ if (obj->defined) {
+ _trace_printf ("sf%ld ", obj->token);
+ } else {
+ _trace_printf ("%d index ",
+ current_stack_depth - obj->operand - 1);
+ }
+ }
+}
+
+static long
+_create_scaled_font_id (cairo_scaled_font_t *font)
+{
+ Object *obj;
+
+ assert(_get_object (SCALED_FONT, font) == NULL);
+ obj = _type_object_create (SCALED_FONT, font);
+ DLCALL (cairo_scaled_font_set_user_data,
+ font, &destroy_key, obj, _object_undef);
+
+ return obj->token;
+}
+
+static cairo_bool_t
+_has_scaled_font_id (const cairo_scaled_font_t *font)
+{
+ return _has_id (SCALED_FONT, font);
+}
+
+static Object *
+_create_surface (cairo_surface_t *surface)
+{
+ Object *obj;
+
+ obj = _get_object (SURFACE, surface);
+ if (obj == NULL) {
+ obj = _type_object_create (SURFACE, surface);
+ DLCALL (cairo_surface_set_user_data,
+ surface, &destroy_key, obj, _object_undef);
+ }
+
+ return obj;
+}
+
+static long
+_get_surface_id (cairo_surface_t *surface)
+{
+ return _get_id (SURFACE, surface);
+}
+
+static cairo_bool_t
+_matrix_is_identity (const cairo_matrix_t *m)
+{
+ return m->xx == 1. && m->yx == 0. &&
+ m->xy == 0. && m->yy == 1. &&
+ m->x0 == 0. && m->y0 == 0.;
+}
+
+#define BUFFER_SIZE 16384
+struct _data_stream {
+ z_stream zlib_stream;
+ unsigned char zin_buf[BUFFER_SIZE];
+ unsigned char zout_buf[BUFFER_SIZE];
+ unsigned char four_tuple[4];
+ int base85_pending;
+};
+
+static void
+_write_zlib_data_start (struct _data_stream *stream)
+{
+ stream->zlib_stream.zalloc = Z_NULL;
+ stream->zlib_stream.zfree = Z_NULL;
+ stream->zlib_stream.opaque = Z_NULL;
+
+ deflateInit (&stream->zlib_stream, Z_DEFAULT_COMPRESSION);
+
+ stream->zlib_stream.next_in = stream->zin_buf;
+ stream->zlib_stream.avail_in = 0;
+ stream->zlib_stream.next_out = stream->zout_buf;
+ stream->zlib_stream.avail_out = BUFFER_SIZE;
+}
+
+static void
+_write_base85_data_start (struct _data_stream *stream)
+{
+ stream->base85_pending = 0;
+}
+
+static cairo_bool_t
+_expand_four_tuple_to_five (unsigned char four_tuple[4],
+ unsigned char five_tuple[5])
+{
+ uint32_t value;
+ int digit, i;
+ cairo_bool_t all_zero = TRUE;
+
+ value = four_tuple[0] << 24 |
+ four_tuple[1] << 16 |
+ four_tuple[2] << 8 |
+ four_tuple[3] << 0;
+ for (i = 0; i < 5; i++) {
+ digit = value % 85;
+ if (digit != 0 && all_zero)
+ all_zero = FALSE;
+ five_tuple[4-i] = digit + 33;
+ value = value / 85;
+ }
+
+ return all_zero;
+}
+
+static void
+_write_base85_data (struct _data_stream *stream,
+ const unsigned char *data,
+ unsigned long length)
+{
+ unsigned char five_tuple[5];
+ int ret;
+
+ assert (_should_trace ());
+
+ while (length--) {
+ stream->four_tuple[stream->base85_pending++] = *data++;
+ if (stream->base85_pending == 4) {
+ if (_expand_four_tuple_to_five (stream->four_tuple, five_tuple))
+ ret = fwrite ("z", 1, 1, logfile);
+ else
+ ret = fwrite (five_tuple, 5, 1, logfile);
+ (void)ret;
+ stream->base85_pending = 0;
+ }
+ }
+}
+
+static void
+_write_zlib_data (struct _data_stream *stream, cairo_bool_t flush)
+{
+ cairo_bool_t finished;
+
+ do {
+ int ret = deflate (&stream->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH);
+ if (flush || stream->zlib_stream.avail_out == 0) {
+ _write_base85_data (stream,
+ stream->zout_buf,
+ BUFFER_SIZE - stream->zlib_stream.avail_out);
+ stream->zlib_stream.next_out = stream->zout_buf;
+ stream->zlib_stream.avail_out = BUFFER_SIZE;
+ }
+
+ finished = TRUE;
+ if (stream->zlib_stream.avail_in != 0)
+ finished = FALSE;
+ if (flush && ret != Z_STREAM_END)
+ finished = FALSE;
+ } while (! finished);
+
+ stream->zlib_stream.next_in = stream->zin_buf;
+}
+
+static void
+_write_data_start (struct _data_stream *stream, uint32_t len)
+{
+ _write_zlib_data_start (stream);
+ _write_base85_data_start (stream);
+
+ _trace_printf ("<|");
+ len = to_be32 (len);
+ _write_base85_data (stream, (unsigned char *) &len, sizeof (len));
+}
+
+static void
+_write_data (struct _data_stream *stream,
+ const void *data,
+ unsigned int length)
+{
+ unsigned int count;
+ const unsigned char *p = data;
+
+ while (length) {
+ count = length;
+ if (count > BUFFER_SIZE - stream->zlib_stream.avail_in)
+ count = BUFFER_SIZE - stream->zlib_stream.avail_in;
+ memcpy (stream->zin_buf + stream->zlib_stream.avail_in, p, count);
+ p += count;
+ stream->zlib_stream.avail_in += count;
+ length -= count;
+
+ if (stream->zlib_stream.avail_in == BUFFER_SIZE)
+ _write_zlib_data (stream, FALSE);
+ }
+}
+
+static void
+_write_zlib_data_end (struct _data_stream *stream)
+{
+ _write_zlib_data (stream, TRUE);
+ deflateEnd (&stream->zlib_stream);
+
+}
+
+static void
+_write_base85_data_end (struct _data_stream *stream)
+{
+ unsigned char five_tuple[5];
+ int ret;
+
+ assert (_should_trace ());
+
+ if (stream->base85_pending) {
+ memset (stream->four_tuple + stream->base85_pending,
+ 0, 4 - stream->base85_pending);
+ _expand_four_tuple_to_five (stream->four_tuple, five_tuple);
+ ret = fwrite (five_tuple, stream->base85_pending+1, 1, logfile);
+ (void) ret;
+ }
+}
+
+static void
+_write_data_end (struct _data_stream *stream)
+{
+ _write_zlib_data_end (stream);
+ _write_base85_data_end (stream);
+
+ _trace_printf ("~>");
+}
+
+static void
+_emit_data (const void *data, unsigned int length)
+{
+ struct _data_stream stream;
+
+ _write_data_start (&stream, length);
+ _write_data (&stream, data, length);
+ _write_data_end (&stream);
+}
+
+static const char *
+_format_to_string (cairo_format_t format)
+{
+#define f(name) case CAIRO_FORMAT_ ## name: return #name
+ switch (format) {
+ f(INVALID);
+ f(ARGB32);
+ f(RGB30);
+ f(RGB24);
+ f(RGB16_565);
+ f(A8);
+ f(A1);
+ }
+#undef f
+ return "UNKNOWN_FORMAT";
+}
+
+static const char *
+_format_to_content_string (cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_INVALID:
+ return "INVALID";
+ case CAIRO_FORMAT_ARGB32:
+ return "COLOR_ALPHA";
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_RGB16_565:
+ return "COLOR";
+ case CAIRO_FORMAT_A8:
+ case CAIRO_FORMAT_A1:
+ return "ALPHA";
+ }
+ return "UNKNOWN";
+}
+
+static const char *
+_status_to_string (cairo_status_t status)
+{
+#define f(name) case CAIRO_STATUS_ ## name: return "STATUS_" #name
+ switch (status) {
+ f(SUCCESS);
+ f(NO_MEMORY);
+ f(INVALID_RESTORE);
+ f(INVALID_POP_GROUP);
+ f(NO_CURRENT_POINT);
+ f(INVALID_MATRIX);
+ f(INVALID_STATUS);
+ f(NULL_POINTER);
+ f(INVALID_STRING);
+ f(INVALID_PATH_DATA);
+ f(READ_ERROR);
+ f(WRITE_ERROR);
+ f(SURFACE_FINISHED);
+ f(SURFACE_TYPE_MISMATCH);
+ f(PATTERN_TYPE_MISMATCH);
+ f(INVALID_CONTENT);
+ f(INVALID_FORMAT);
+ f(INVALID_VISUAL);
+ f(FILE_NOT_FOUND);
+ f(INVALID_DASH);
+ f(INVALID_DSC_COMMENT);
+ f(INVALID_INDEX);
+ f(CLIP_NOT_REPRESENTABLE);
+ f(TEMP_FILE_ERROR);
+ f(INVALID_STRIDE);
+ f(FONT_TYPE_MISMATCH);
+ f(USER_FONT_IMMUTABLE);
+ f(USER_FONT_ERROR);
+ f(NEGATIVE_COUNT);
+ f(INVALID_CLUSTERS);
+ f(INVALID_SLANT);
+ f(INVALID_WEIGHT);
+ f(INVALID_SIZE);
+ f(USER_FONT_NOT_IMPLEMENTED);
+ f(DEVICE_TYPE_MISMATCH);
+ f(DEVICE_ERROR);
+ f(INVALID_MESH_CONSTRUCTION);
+ f(DEVICE_FINISHED);
+ f(JBIG2_GLOBAL_MISSING);
+ case CAIRO_STATUS_LAST_STATUS:
+ break;
+ }
+ return "UNKNOWN_STATUS";
+#undef f
+}
+
+static void CAIRO_PRINTF_FORMAT(2, 3)
+_emit_image (cairo_surface_t *image,
+ const char *info,
+ ...)
+{
+ int stride, row, width, height;
+ uint32_t len;
+ cairo_format_t format;
+ uint8_t row_stack[BUFFER_SIZE];
+ uint8_t *rowdata;
+ uint8_t *data;
+ struct _data_stream stream;
+ cairo_status_t status;
+
+ status = DLCALL (cairo_surface_status, image);
+ if (status) {
+ _trace_printf ("<< /status //%s >> image",
+ _status_to_string (status));
+ return;
+ }
+
+ width = DLCALL (cairo_image_surface_get_width, image);
+ height = DLCALL (cairo_image_surface_get_height, image);
+ stride = DLCALL (cairo_image_surface_get_stride, image);
+ format = DLCALL (cairo_image_surface_get_format, image);
+ data = DLCALL (cairo_image_surface_get_data, image);
+
+ _trace_printf ("dict\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " /format //%s set\n",
+ width, height,
+ _format_to_string (format));
+ if (info != NULL) {
+ va_list ap;
+
+ va_start (ap, info);
+ _trace_vprintf (info, ap);
+ va_end (ap);
+ }
+
+ if (DLCALL (cairo_version) >= CAIRO_VERSION_ENCODE (1, 9, 0)) {
+ const char *mime_types[] = {
+ CAIRO_MIME_TYPE_JPEG,
+ CAIRO_MIME_TYPE_JP2,
+ CAIRO_MIME_TYPE_PNG,
+ NULL
+ }, **mime_type;
+
+ for (mime_type = mime_types; *mime_type; mime_type++) {
+ const unsigned char *mime_data;
+ unsigned long mime_length;
+
+ DLCALL (cairo_surface_get_mime_data,
+ image, *mime_type, &mime_data, &mime_length);
+ if (mime_data != NULL) {
+ _trace_printf (" /mime-type (%s) set\n"
+ " /source <~",
+ *mime_type);
+ _write_base85_data_start (&stream);
+ _write_base85_data (&stream, mime_data, mime_length);
+ _write_base85_data_end (&stream);
+ _trace_printf ("~> set\n"
+ " image");
+ return;
+ }
+ }
+ }
+
+ switch (format) {
+ case CAIRO_FORMAT_A1: len = (width + 7)/8; break;
+ case CAIRO_FORMAT_A8: len = width; break;
+ case CAIRO_FORMAT_RGB16_565: len = 2*width; break;
+ case CAIRO_FORMAT_RGB24: len = 3*width; break;
+ default:
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_INVALID:
+ case CAIRO_FORMAT_ARGB32: len = 4*width; break;
+ }
+
+ _trace_printf (" /source ");
+ _write_data_start (&stream, len * height);
+
+#ifdef WORDS_BIGENDIAN
+ switch (format) {
+ case CAIRO_FORMAT_A1:
+ for (row = height; row--; ) {
+ _write_data (&stream, data, (width+7)/8);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_A8:
+ for (row = height; row--; ) {
+ _write_data (&stream, data, width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_RGB16_565:
+ for (row = height; row--; ) {
+ _write_data (&stream, data, 2*width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_RGB24:
+ for (row = height; row--; ) {
+ int col;
+ rowdata = data;
+ for (col = width; col--; ) {
+ _write_data (&stream, rowdata, 3);
+ rowdata+=4;
+ }
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_ARGB32:
+ for (row = height; row--; ) {
+ _write_data (&stream, data, 4*width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ break;
+ }
+#else
+ if (stride > ARRAY_LENGTH (row_stack)) {
+ rowdata = malloc (stride);
+ if (rowdata == NULL)
+ goto BAIL;
+ } else
+ rowdata = row_stack;
+
+ switch (format) {
+ case CAIRO_FORMAT_A1:
+ for (row = height; row--; ) {
+ int col;
+ for (col = 0; col < (width + 7)/8; col++)
+ rowdata[col] = CAIRO_BITSWAP8 (data[col]);
+ _write_data (&stream, rowdata, (width+7)/8);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_A8:
+ for (row = height; row--; ) {
+ _write_data (&stream, rowdata, width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_RGB16_565: /* XXX endianness */
+ for (row = height; row--; ) {
+ uint16_t *src = (uint16_t *) data;
+ uint16_t *dst = (uint16_t *)rowdata;
+ int col;
+ for (col = 0; col < width; col++)
+ dst[col] = bswap_16 (src[col]);
+ _write_data (&stream, rowdata, 2*width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_RGB24:
+ for (row = height; row--; ) {
+ uint8_t *src = data;
+ int col;
+ for (col = 0; col < width; col++) {
+ rowdata[3*col+2] = *src++;
+ rowdata[3*col+1] = *src++;
+ rowdata[3*col+0] = *src++;
+ src++;
+ }
+ _write_data (&stream, rowdata, 3*width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_RGB30:
+ case CAIRO_FORMAT_ARGB32:
+ for (row = height; row--; ) {
+ uint32_t *src = (uint32_t *) data;
+ uint32_t *dst = (uint32_t *) rowdata;
+ int col;
+ for (col = 0; col < width; col++)
+ dst[col] = bswap_32 (src[col]);
+ _write_data (&stream, rowdata, 4*width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ break;
+ }
+ if (rowdata != row_stack)
+ free (rowdata);
+
+BAIL:
+ _write_data_end (&stream);
+#endif
+ _trace_printf (" set\n image");
+}
+
+static void
+_encode_string_literal (char *out, int max,
+ const char *utf8, int len)
+{
+ char c;
+ const char *end;
+
+ *out++ = '(';
+ max--;
+
+ if (utf8 == NULL)
+ goto DONE;
+
+ if (len < 0)
+ len = strlen (utf8);
+ end = utf8 + len;
+
+ while (utf8 < end) {
+ if (max < 5)
+ break;
+
+ switch ((c = *utf8++)) {
+ case '\n':
+ *out++ = '\\';
+ *out++ = 'n';
+ max -= 2;
+ break;
+ case '\r':
+ *out++ = '\\';
+ *out++ = 'r';
+ max -= 2;
+ case '\t':
+ *out++ = '\\';
+ *out++ = 't';
+ max -= 2;
+ break;
+ case '\b':
+ *out++ = '\\';
+ *out++ = 'b';
+ max -= 2;
+ break;
+ case '\f':
+ *out++ = '\\';
+ *out++ = 'f';
+ max -= 2;
+ break;
+ case '\\':
+ case '(':
+ case ')':
+ *out++ = '\\';
+ *out++ = c;
+ max -= 2;
+ break;
+ default:
+ if (isprint (c) || isspace (c)) {
+ *out++ = c;
+ } else {
+ int octal = 0;
+ while (c) {
+ octal *= 10;
+ octal += c&7;
+ c /= 8;
+ }
+ octal = snprintf (out, max, "\\%03d", octal);
+ out += octal;
+ max -= octal;
+ }
+ break;
+ }
+ }
+DONE:
+ *out++ = ')';
+ *out = '\0';
+}
+
+static void
+to_octal (int value, char *buf, size_t size)
+{
+ do {
+ buf[--size] = '0' + (value & 7);
+ value >>= 3;
+ } while (size);
+}
+
+static void
+_emit_string_literal (const char *utf8, int len)
+{
+ char c;
+ const char *end;
+
+ if (utf8 == NULL) {
+ _trace_printf ("()");
+ return;
+ }
+
+ if (len < 0)
+ len = strlen (utf8);
+ end = utf8 + len;
+
+ _trace_printf ("(");
+ while (utf8 < end) {
+ switch ((c = *utf8++)) {
+ case '\n':
+ c = 'n';
+ goto ESCAPED_CHAR;
+ case '\r':
+ c = 'r';
+ goto ESCAPED_CHAR;
+ case '\t':
+ c = 't';
+ goto ESCAPED_CHAR;
+ case '\b':
+ c = 'b';
+ goto ESCAPED_CHAR;
+ case '\f':
+ c = 'f';
+ goto ESCAPED_CHAR;
+ case '\\':
+ case '(':
+ case ')':
+ESCAPED_CHAR:
+ _trace_printf ("\\%c", c);
+ break;
+ default:
+ if (isprint (c) || isspace (c)) {
+ _trace_printf ("%c", c);
+ } else {
+ char buf[4] = { '\\' };
+ int ret_ignored;
+
+ to_octal (c, buf+1, 3);
+ ret_ignored = fwrite (buf, 4, 1, logfile);
+ (void)ret_ignored;
+ }
+ break;
+ }
+ }
+ _trace_printf (")");
+}
+
+static void
+_emit_current (Object *obj)
+{
+ if (obj != NULL && ! _pop_operands_to_object (obj)) {
+ if (obj->operand != -1) {
+ int n;
+
+ _trace_printf ("%d -1 roll %% %s%ld\n",
+ current_stack_depth - obj->operand,
+ obj->type->op_code, obj->token);
+
+ for (n = obj->operand; n < current_stack_depth - 1; n++) {
+ current_object[n] = current_object[n+1];
+ current_object[n]->operand = n;
+ }
+ obj->operand = -1;
+ current_stack_depth--;
+ } else {
+ assert(obj->defined);
+ _trace_printf ("%s%ld\n", obj->type->op_code, obj->token);
+ }
+
+ _push_object (obj);
+ dump_stack(__func__);
+ }
+}
+
+static void
+_emit_context (cairo_t *cr)
+{
+ _emit_current (_get_object (CONTEXT, cr));
+}
+
+static void
+_emit_pattern (cairo_pattern_t *pattern)
+{
+ _emit_current (_get_object (PATTERN, pattern));
+}
+
+static void
+_emit_surface (cairo_surface_t *surface)
+{
+ _emit_current (_get_object (SURFACE, surface));
+}
+
+static void CAIRO_PRINTF_FORMAT(2, 3)
+_emit_cairo_op (cairo_t *cr, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (cr == NULL || ! _write_lock ())
+ return;
+
+ _emit_context (cr);
+
+ va_start (ap, fmt);
+ _trace_vprintf ( fmt, ap);
+ va_end (ap);
+
+ _write_unlock ();
+}
+
+cairo_t *
+cairo_create (cairo_surface_t *target)
+{
+ cairo_t *ret;
+ long surface_id;
+ long context_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_create, target);
+ context_id = _create_context_id (ret);
+
+ _emit_line_info ();
+ if (target != NULL && _write_lock ()) {
+ surface_id = _get_surface_id (target);
+ if (surface_id != -1) {
+ _get_object (SURFACE, target)->foreign = FALSE;
+
+ /* we presume that we will continue to use the context */
+ if (_pop_operands_to (SURFACE, target)){
+ _consume_operand (false);
+ } else {
+ _trace_printf ("s%ld ", surface_id);
+ }
+ _trace_printf ("context %% c%ld\n", context_id);
+ _push_operand (CONTEXT, ret);
+ dump_stack(__func__);
+ }
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_save (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "save\n");
+ DLCALL (cairo_save, cr);
+ _exit_trace ();
+}
+
+void
+cairo_restore (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "restore\n");
+ DLCALL (cairo_restore, cr);
+ _exit_trace ();
+}
+
+void
+cairo_push_group (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "//COLOR_ALPHA push-group\n");
+ DLCALL (cairo_push_group, cr);
+ _exit_trace ();
+}
+
+static const char *
+_content_to_string (cairo_content_t content)
+{
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA: return "ALPHA";
+ case CAIRO_CONTENT_COLOR: return "COLOR";
+ default:
+ case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
+ }
+}
+
+void
+cairo_push_group_with_content (cairo_t *cr, cairo_content_t content)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "//%s push-group\n", _content_to_string (content));
+ DLCALL (cairo_push_group_with_content, cr, content);
+ _exit_trace ();
+}
+
+cairo_pattern_t *
+cairo_pop_group (cairo_t *cr)
+{
+ cairo_pattern_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pop_group, cr);
+
+ _emit_line_info ();
+ _emit_cairo_op (cr, "pop-group %% p%ld\n", _create_pattern_id (ret));
+ _push_operand (PATTERN, ret);
+ dump_stack(__func__);
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_pop_group_to_source (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "pop-group set-source\n");
+ DLCALL (cairo_pop_group_to_source, cr);
+ _exit_trace ();
+}
+
+static const char *
+_operator_to_string (cairo_operator_t op)
+{
+#define f(name) case CAIRO_OPERATOR_ ## name: return #name
+ switch (op) {
+ f(OVER);
+ f(SOURCE);
+ f(CLEAR);
+ f(IN);
+ f(OUT);
+ f(ATOP);
+ f(DEST);
+ f(DEST_OVER);
+ f(DEST_IN);
+ f(DEST_OUT);
+ f(DEST_ATOP);
+ f(XOR);
+ f(ADD);
+ f(SATURATE);
+ f(MULTIPLY);
+ f(SCREEN);
+ f(OVERLAY);
+ f(DARKEN);
+ f(LIGHTEN);
+ case CAIRO_OPERATOR_COLOR_DODGE: return "DODGE";
+ case CAIRO_OPERATOR_COLOR_BURN: return "BURN";
+ f(HARD_LIGHT);
+ f(SOFT_LIGHT);
+ f(DIFFERENCE);
+ f(EXCLUSION);
+ f(HSL_HUE);
+ f(HSL_SATURATION);
+ f(HSL_COLOR);
+ f(HSL_LUMINOSITY);
+ }
+#undef f
+ return "UNKNOWN_OPERATOR";
+}
+
+void
+cairo_set_operator (cairo_t *cr, cairo_operator_t op)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "//%s set-operator\n", _operator_to_string (op));
+ DLCALL (cairo_set_operator, cr, op);
+ _exit_trace ();
+}
+
+void
+cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g %g %g set-source-rgb\n", red, green, blue);
+ DLCALL (cairo_set_source_rgb, cr, red, green, blue);
+ _exit_trace ();
+}
+
+void
+cairo_set_source_rgba (cairo_t *cr, double red, double green, double blue, double alpha)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g %g %g %g set-source-rgba\n",
+ red, green, blue, alpha);
+ DLCALL (cairo_set_source_rgba, cr, red, green, blue, alpha);
+ _exit_trace ();
+}
+
+static void
+_emit_source_image (cairo_surface_t *surface)
+{
+ Object *obj;
+ cairo_surface_t *image;
+ cairo_t *cr;
+
+ obj = _get_object (SURFACE, surface);
+ if (obj == NULL)
+ return;
+
+ image = DLCALL (cairo_image_surface_create,
+ CAIRO_FORMAT_ARGB32,
+ obj->width,
+ obj->height);
+ cr = DLCALL (cairo_create, image);
+ DLCALL (cairo_set_source_surface, cr, surface, 0, 0);
+ DLCALL (cairo_paint, cr);
+ DLCALL (cairo_destroy, cr);
+
+ _emit_image (image, NULL);
+ _trace_printf (" set-source-image ");
+ DLCALL (cairo_surface_destroy, image);
+
+ obj->foreign = FALSE;
+}
+
+static void
+_emit_source_image_rectangle (cairo_surface_t *surface,
+ int x, int y,
+ int width, int height)
+{
+ Object *obj;
+ cairo_surface_t *image;
+ cairo_t *cr;
+
+ obj = _get_object (SURFACE, surface);
+ if (obj == NULL)
+ return;
+
+ if (obj->foreign) {
+ _emit_source_image (surface);
+ return;
+ }
+
+ image = DLCALL (cairo_image_surface_create,
+ CAIRO_FORMAT_ARGB32,
+ width,
+ height);
+ cr = DLCALL (cairo_create, image);
+ DLCALL (cairo_set_source_surface, cr, surface, x, y);
+ DLCALL (cairo_paint, cr);
+ DLCALL (cairo_destroy, cr);
+
+ _emit_image (image, NULL);
+ _trace_printf (" %d %d set-device-offset set-source-image ",
+ x, y);
+ DLCALL (cairo_surface_destroy, image);
+}
+
+void
+cairo_set_source_surface (cairo_t *cr, cairo_surface_t *surface, double x, double y)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && surface != NULL && _write_lock ()) {
+ Object *obj = _get_object (SURFACE, surface);
+
+ if (_is_current (SURFACE, surface, 0) &&
+ _is_current (CONTEXT, cr, 1))
+ {
+ _consume_operand (false);
+ }
+ else if (_is_current (SURFACE, surface, 1) &&
+ _is_current (CONTEXT, cr, 0) &&
+ obj->defined)
+ {
+ _trace_printf ("exch ");
+ _exch_operands ();
+ _consume_operand (false);
+ } else if (obj->defined) {
+ _emit_context (cr);
+ _trace_printf ("s%ld ", obj->token);
+ } else {
+ _emit_context (cr);
+ _trace_printf ("%d index ",
+ current_stack_depth - obj->operand - 1);
+ }
+
+ if (obj->foreign)
+ _emit_source_image (surface);
+
+ _trace_printf ("pattern");
+ if (x != 0. || y != 0.)
+ _trace_printf (" %g %g translate", -x, -y);
+
+ _trace_printf (" set-source\n");
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_set_source_surface, cr, surface, x, y);
+ _exit_trace ();
+}
+
+void
+cairo_set_source (cairo_t *cr, cairo_pattern_t *source)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && source != NULL && _write_lock ()) {
+ Object *obj = _get_object (PATTERN, source);
+ cairo_bool_t need_context_and_pattern = TRUE;
+
+ if (_is_current (PATTERN, source, 0) &&
+ _is_current (CONTEXT, cr, 1))
+ {
+ if (obj->defined) {
+ _consume_operand (false);
+ } else {
+ _trace_printf ("exch 1 index ");
+ _exch_operands ();
+ }
+ need_context_and_pattern = FALSE;
+ }
+ else if (_is_current (PATTERN, source, 1) &&
+ _is_current (CONTEXT, cr, 0))
+ {
+ if (obj->defined) {
+ _trace_printf ("exch ");
+ _exch_operands ();
+ _consume_operand (false);
+ need_context_and_pattern = FALSE;
+ }
+ }
+
+ if (need_context_and_pattern) {
+ _emit_context (cr);
+ _emit_pattern_id (source);
+ }
+
+ _trace_printf ("set-source %% p%ld\n", obj->token);
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_set_source, cr, source);
+ _exit_trace ();
+}
+
+cairo_pattern_t *
+cairo_get_source (cairo_t *cr)
+{
+ cairo_pattern_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_get_source, cr);
+
+ if (! _has_pattern_id (ret)) {
+ _emit_cairo_op (cr, "/source get /p%ld exch def\n",
+ _create_pattern_id (ret));
+ _get_object (PATTERN, ret)->defined = TRUE;
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_set_tolerance (cairo_t *cr, double tolerance)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g set-tolerance\n", tolerance);
+ DLCALL (cairo_set_tolerance, cr, tolerance);
+ _exit_trace ();
+}
+
+static const char *
+_antialias_to_string (cairo_antialias_t antialias)
+{
+#define f(name) case CAIRO_ANTIALIAS_ ## name: return "ANTIALIAS_" #name
+ switch (antialias) {
+ f(DEFAULT);
+
+ f(NONE);
+ f(GRAY);
+ f(SUBPIXEL);
+
+ f(FAST);
+ f(GOOD);
+ f(BEST);
+ };
+#undef f
+ return "UNKNOWN_ANTIALIAS";
+}
+
+void
+cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr,
+ "//%s set-antialias\n", _antialias_to_string (antialias));
+ DLCALL (cairo_set_antialias, cr, antialias);
+ _exit_trace ();
+}
+
+static const char *
+_fill_rule_to_string (cairo_fill_rule_t rule)
+{
+#define f(name) case CAIRO_FILL_RULE_ ## name: return #name
+ switch (rule) {
+ f(WINDING);
+ f(EVEN_ODD);
+ };
+#undef f
+ return "UNKNOWN_FILL_RULE";
+}
+
+void
+cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr,
+ "//%s set-fill-rule\n", _fill_rule_to_string (fill_rule));
+ DLCALL (cairo_set_fill_rule, cr, fill_rule);
+ _exit_trace ();
+}
+
+void
+cairo_set_line_width (cairo_t *cr, double width)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g set-line-width\n", width);
+ DLCALL (cairo_set_line_width, cr, width);
+ _exit_trace ();
+}
+
+static const char *
+_line_cap_to_string (cairo_line_cap_t line_cap)
+{
+#define f(name) case CAIRO_LINE_CAP_ ## name: return "LINE_CAP_" #name
+ switch (line_cap) {
+ f(BUTT);
+ f(ROUND);
+ f(SQUARE);
+ };
+#undef f
+ return "UNKNOWN_LINE_CAP";
+}
+
+void
+cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "//%s set-line-cap\n", _line_cap_to_string (line_cap));
+ DLCALL (cairo_set_line_cap, cr, line_cap);
+ _exit_trace ();
+}
+
+static const char *
+_line_join_to_string (cairo_line_join_t line_join)
+{
+#define f(name) case CAIRO_LINE_JOIN_ ## name: return "LINE_JOIN_" #name
+ switch (line_join) {
+ f(MITER);
+ f(ROUND);
+ f(BEVEL);
+ };
+#undef f
+ return "UNKNOWN_LINE_JOIN";
+}
+
+void
+cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr,
+ "//%s set-line-join\n", _line_join_to_string (line_join));
+ DLCALL (cairo_set_line_join, cr, line_join);
+ _exit_trace ();
+}
+
+void
+cairo_set_dash (cairo_t *cr, const double *dashes, int num_dashes, double offset)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && _write_lock ()) {
+ int n;
+
+ _emit_context (cr);
+
+ _trace_printf ("[");
+ for (n = 0; n < num_dashes; n++) {
+ if (n != 0)
+ _trace_printf (" ");
+ _trace_printf ("%g", dashes[n]);
+ }
+ _trace_printf ("] %g set-dash\n", offset);
+
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_set_dash, cr, dashes, num_dashes, offset);
+ _exit_trace ();
+}
+
+void
+cairo_set_miter_limit (cairo_t *cr, double limit)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g set-miter-limit\n", limit);
+ DLCALL (cairo_set_miter_limit, cr, limit);
+ _exit_trace ();
+}
+
+void
+cairo_translate (cairo_t *cr, double tx, double ty)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g %g translate\n", tx, ty);
+ DLCALL (cairo_translate, cr, tx, ty);
+ _exit_trace ();
+}
+
+void
+cairo_scale (cairo_t *cr, double sx, double sy)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g %g scale\n", sx, sy);
+ DLCALL (cairo_scale, cr, sx, sy);
+ _exit_trace ();
+}
+
+void
+cairo_rotate (cairo_t *cr, double angle)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g rotate\n", angle);
+ DLCALL (cairo_rotate, cr, angle);
+ _exit_trace ();
+}
+
+void
+cairo_transform (cairo_t *cr, const cairo_matrix_t *matrix)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g %g %g %g %g %g matrix transform\n",
+ matrix->xx, matrix->yx,
+ matrix->xy, matrix->yy,
+ matrix->x0, matrix->y0);
+ DLCALL (cairo_transform, cr, matrix);
+ _exit_trace ();
+}
+
+void
+cairo_set_matrix (cairo_t *cr, const cairo_matrix_t *matrix)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (_matrix_is_identity (matrix)) {
+ _emit_cairo_op (cr, "identity set-matrix\n");
+ } else {
+ _emit_cairo_op (cr, "%g %g %g %g %g %g matrix set-matrix\n",
+ matrix->xx, matrix->yx,
+ matrix->xy, matrix->yy,
+ matrix->x0, matrix->y0);
+ }
+ DLCALL (cairo_set_matrix, cr, matrix);
+ _exit_trace ();
+}
+
+cairo_surface_t *
+cairo_get_target (cairo_t *cr)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_get_target, cr);
+ if (cr != NULL) {
+ Object *obj = _create_surface (ret);
+
+ if (! obj->defined) {
+ _emit_cairo_op (cr,
+ "/target get /s%ld exch def\n",
+ obj->token);
+ obj->defined = TRUE;
+ }
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_get_group_target (cairo_t *cr)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_get_group_target, cr);
+ if (cr != NULL) {
+ Object *obj = _create_surface (ret);
+
+ if (! obj->defined) {
+ _emit_cairo_op (cr,
+ "/group-target get /s%ld exch def\n",
+ obj->token);
+ obj->defined = TRUE;
+ }
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_identity_matrix (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "identity set-matrix\n");
+ DLCALL (cairo_identity_matrix, cr);
+ _exit_trace ();
+}
+
+void
+cairo_new_path (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "n ");
+ DLCALL (cairo_new_path, cr);
+ _exit_trace ();
+}
+
+void
+cairo_move_to (cairo_t *cr, double x, double y)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g m ", x, y);
+ DLCALL (cairo_move_to, cr, x, y);
+ _exit_trace ();
+}
+
+void
+cairo_new_sub_path (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "N ");
+ DLCALL (cairo_new_sub_path, cr);
+ _exit_trace ();
+}
+
+void
+cairo_line_to (cairo_t *cr, double x, double y)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g l ", x, y);
+ DLCALL (cairo_line_to, cr, x, y);
+ _exit_trace ();
+}
+
+void
+cairo_curve_to (cairo_t *cr, double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g %g %g %g %g c ", x1, y1, x2, y2, x3, y3);
+ DLCALL (cairo_curve_to, cr, x1, y1, x2, y2, x3, y3);
+ _exit_trace ();
+}
+
+void
+cairo_arc (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g %g %g %g arc\n", xc, yc, radius, angle1, angle2);
+ DLCALL (cairo_arc, cr, xc, yc, radius, angle1, angle2);
+ _exit_trace ();
+}
+
+void
+cairo_arc_negative (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g %g %g %g arc-\n",
+ xc, yc, radius, angle1, angle2);
+ DLCALL (cairo_arc_negative, cr, xc, yc, radius, angle1, angle2);
+ _exit_trace ();
+}
+
+void
+cairo_rel_move_to (cairo_t *cr, double dx, double dy)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g M ", dx, dy);
+ DLCALL (cairo_rel_move_to, cr, dx, dy);
+ _exit_trace ();
+}
+
+void
+cairo_rel_line_to (cairo_t *cr, double dx, double dy)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g L ", dx, dy);
+ DLCALL (cairo_rel_line_to, cr, dx, dy);
+ _exit_trace ();
+}
+
+void
+cairo_rel_curve_to (cairo_t *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g %g %g %g %g C ",
+ dx1, dy1, dx2, dy2, dx3, dy3);
+ DLCALL (cairo_rel_curve_to, cr, dx1, dy1, dx2, dy2, dx3, dy3);
+ _exit_trace ();
+}
+
+void
+cairo_rectangle (cairo_t *cr, double x, double y, double width, double height)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "%g %g %g %g rectangle\n", x, y, width, height);
+ DLCALL (cairo_rectangle, cr, x, y, width, height);
+ _exit_trace ();
+}
+
+void
+cairo_close_path (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_cairo_op (cr, "h\n");
+ DLCALL (cairo_close_path, cr);
+ _exit_trace ();
+}
+
+void
+cairo_paint (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "paint\n");
+ DLCALL (cairo_paint, cr);
+ _exit_trace ();
+}
+
+void
+cairo_paint_with_alpha (cairo_t *cr, double alpha)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g paint-with-alpha\n", alpha);
+ DLCALL (cairo_paint_with_alpha, cr, alpha);
+ _exit_trace ();
+}
+
+void
+cairo_mask (cairo_t *cr, cairo_pattern_t *pattern)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && pattern != NULL && _write_lock ()) {
+ Object *obj = _get_object (PATTERN, pattern);
+ cairo_bool_t need_context_and_pattern = TRUE;
+
+ if (_is_current (PATTERN, pattern, 0) &&
+ _is_current (CONTEXT, cr, 1))
+ {
+ if (obj->defined) {
+ _consume_operand (false);
+ need_context_and_pattern = FALSE;
+ }
+ }
+ else if (_is_current (PATTERN, pattern, 1) &&
+ _is_current (CONTEXT, cr, 0))
+ {
+ if (obj->defined) {
+ _trace_printf ("exch ");
+ _exch_operands ();
+ _consume_operand (false);
+ need_context_and_pattern = FALSE;
+ }
+ }
+
+ if (need_context_and_pattern) {
+ _emit_context (cr);
+ _emit_pattern_id (pattern);
+ }
+
+ _trace_printf (" mask\n");
+ _write_unlock ();
+ }
+ DLCALL (cairo_mask, cr, pattern);
+ _exit_trace ();
+}
+
+void
+cairo_mask_surface (cairo_t *cr, cairo_surface_t *surface, double x, double y)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && surface != NULL && _write_lock ()) {
+ Object *obj = _get_object (SURFACE, surface);
+ if (_is_current (SURFACE, surface, 0) &&
+ _is_current (CONTEXT, cr, 1))
+ {
+ _consume_operand (false);
+ }
+ else if (_is_current (SURFACE, surface, 1) &&
+ _is_current (CONTEXT, cr, 0))
+ {
+ _trace_printf ("exch ");
+ _exch_operands ();
+ _consume_operand (false);
+ } else if (obj->defined){
+ _emit_context (cr);
+ _trace_printf ("s%ld ", obj->token);
+ } else {
+ _emit_context (cr);
+ _trace_printf ("%d index ",
+ current_stack_depth - obj->operand - 1);
+ }
+ _trace_printf ("pattern");
+
+ if (x != 0. || y != 0.)
+ _trace_printf (" %g %g translate", -x, -y);
+
+ _trace_printf (" mask\n");
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_mask_surface, cr, surface, x, y);
+ _exit_trace ();
+}
+
+void
+cairo_stroke (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "stroke\n");
+ DLCALL (cairo_stroke, cr);
+ _exit_trace ();
+}
+
+void
+cairo_stroke_preserve (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "stroke+\n");
+ DLCALL (cairo_stroke_preserve, cr);
+ _exit_trace ();
+}
+
+void
+cairo_fill (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "fill\n");
+ DLCALL (cairo_fill, cr);
+ _exit_trace ();
+}
+
+void
+cairo_fill_preserve (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "fill+\n");
+ DLCALL (cairo_fill_preserve, cr);
+ _exit_trace ();
+}
+
+void
+cairo_copy_page (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "copy-page\n");
+ DLCALL (cairo_copy_page, cr);
+ _exit_trace ();
+}
+
+void
+cairo_show_page (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "show-page\n");
+ DLCALL (cairo_show_page, cr);
+ _exit_trace ();
+}
+
+void
+cairo_clip (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "clip\n");
+ DLCALL (cairo_clip, cr);
+ _exit_trace ();
+}
+
+void
+cairo_clip_preserve (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "clip+\n");
+ DLCALL (cairo_clip_preserve, cr);
+ _exit_trace ();
+}
+
+void
+cairo_reset_clip (cairo_t *cr)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "reset-clip\n");
+ DLCALL (cairo_reset_clip, cr);
+ _exit_trace ();
+}
+
+
+static const char *
+_slant_to_string (cairo_font_slant_t font_slant)
+{
+#define f(name) case CAIRO_FONT_SLANT_ ## name: return "SLANT_" #name
+ switch (font_slant) {
+ f(NORMAL);
+ f(ITALIC);
+ f(OBLIQUE);
+ };
+#undef f
+ return "UNKNOWN_SLANT";
+}
+
+static const char *
+_weight_to_string (cairo_font_weight_t font_weight)
+{
+#define f(name) case CAIRO_FONT_WEIGHT_ ## name: return "WEIGHT_" #name
+ switch (font_weight) {
+ f(NORMAL);
+ f(BOLD);
+ };
+#undef f
+ return "UNKNOWN_WEIGHT";
+}
+
+void
+cairo_select_font_face (cairo_t *cr, const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && _write_lock ()) {
+ _emit_context (cr);
+ _emit_string_literal (family, -1);
+ _trace_printf (" //%s //%s select-font-face\n",
+ _slant_to_string (slant),
+ _weight_to_string (weight));
+ _write_unlock ();
+ }
+ DLCALL (cairo_select_font_face, cr, family, slant, weight);
+ _exit_trace ();
+}
+
+cairo_font_face_t *
+cairo_get_font_face (cairo_t *cr)
+{
+ cairo_font_face_t *ret;
+ long font_face_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_get_font_face, cr);
+ font_face_id = _create_font_face_id (ret);
+
+ _emit_cairo_op (cr, "/font-face get %% f%ld\n", font_face_id);
+ _push_operand (FONT_FACE, ret);
+ dump_stack(__func__);
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && font_face != NULL && _write_lock ()) {
+ if (_is_current (FONT_FACE, font_face, 0) &&
+ _is_current (CONTEXT, cr, 1))
+ {
+ _consume_operand (false);
+ }
+ else if (_is_current (FONT_FACE, font_face, 1) &&
+ _is_current (CONTEXT, cr, 0))
+ {
+ _trace_printf ("exch ");
+ _exch_operands ();
+ _consume_operand (false);
+ }
+ else
+ {
+ _emit_context (cr);
+ _emit_font_face_id (font_face);
+ }
+
+ _trace_printf ("set-font-face\n");
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_set_font_face, cr, font_face);
+ _exit_trace ();
+}
+
+void
+cairo_set_font_size (cairo_t *cr, double size)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g set-font-size\n", size);
+ DLCALL (cairo_set_font_size, cr, size);
+ _exit_trace ();
+}
+
+void
+cairo_set_font_matrix (cairo_t *cr, const cairo_matrix_t *matrix)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_cairo_op (cr, "%g %g %g %g %g %g matrix set-font-matrix\n",
+ matrix->xx, matrix->yx,
+ matrix->xy, matrix->yy,
+ matrix->x0, matrix->y0);
+ DLCALL (cairo_set_font_matrix, cr, matrix);
+ _exit_trace ();
+}
+
+static const char *
+_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order)
+{
+#define f(name) case CAIRO_SUBPIXEL_ORDER_ ## name: return "SUBPIXEL_ORDER_" #name
+ switch (subpixel_order) {
+ f(DEFAULT);
+ f(RGB);
+ f(BGR);
+ f(VRGB);
+ f(VBGR);
+ };
+#undef f
+ return "UNKNOWN_SUBPIXEL_ORDER";
+}
+
+static const char *
+_hint_style_to_string (cairo_hint_style_t hint_style)
+{
+#define f(name) case CAIRO_HINT_STYLE_ ## name: return "HINT_STYLE_" #name
+ switch (hint_style) {
+ f(DEFAULT);
+ f(NONE);
+ f(SLIGHT);
+ f(MEDIUM);
+ f(FULL);
+ };
+#undef f
+ return "UNKNOWN_HINT_STYLE";
+}
+
+static const char *
+_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics)
+{
+#define f(name) case CAIRO_HINT_METRICS_ ## name: return "HINT_METRICS_" #name
+ switch (hint_metrics) {
+ f(DEFAULT);
+ f(OFF);
+ f(ON);
+ };
+#undef f
+ return "UNKNOWN_HINT_METRICS";
+}
+
+static void
+_emit_font_options (const cairo_font_options_t *options)
+{
+ cairo_antialias_t antialias;
+ cairo_subpixel_order_t subpixel_order;
+ cairo_hint_style_t hint_style;
+ cairo_hint_metrics_t hint_metrics;
+
+ _trace_printf ("<<");
+
+ antialias = DLCALL (cairo_font_options_get_antialias, options);
+ if (antialias != CAIRO_ANTIALIAS_DEFAULT) {
+ _trace_printf (" /antialias //%s",
+ _antialias_to_string (antialias));
+ }
+
+ subpixel_order = DLCALL (cairo_font_options_get_subpixel_order, options);
+ if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) {
+ _trace_printf (" /subpixel-order //%s",
+ _subpixel_order_to_string (subpixel_order));
+ }
+
+ hint_style = DLCALL (cairo_font_options_get_hint_style, options);
+ if (hint_style != CAIRO_HINT_STYLE_DEFAULT) {
+ _trace_printf (" /hint-style //%s",
+ _hint_style_to_string (hint_style));
+ }
+
+ hint_metrics = DLCALL (cairo_font_options_get_hint_metrics, options);
+ if (hint_metrics != CAIRO_HINT_METRICS_DEFAULT) {
+ _trace_printf (" /hint-metrics //%s",
+ _hint_metrics_to_string (hint_metrics));
+ }
+
+ _trace_printf (" >>");
+}
+
+void
+cairo_set_font_options (cairo_t *cr, const cairo_font_options_t *options)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && options != NULL && _write_lock ()) {
+ _emit_context (cr);
+ _emit_font_options (options);
+ _trace_printf (" set-font-options\n");
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_set_font_options, cr, options);
+ _exit_trace ();
+}
+
+cairo_scaled_font_t *
+cairo_get_scaled_font (cairo_t *cr)
+{
+ cairo_scaled_font_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_get_scaled_font, cr);
+
+ if (cr != NULL && ! _has_scaled_font_id (ret)) {
+ _emit_cairo_op (cr, "/scaled-font get /sf%ld exch def\n",
+ _create_scaled_font_id (ret));
+ _get_object (SCALED_FONT, ret)->defined = TRUE;
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_set_scaled_font (cairo_t *cr, const cairo_scaled_font_t *scaled_font)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && scaled_font != NULL && _write_lock ()) {
+ Object *obj = _get_object (SCALED_FONT, scaled_font);
+ cairo_bool_t need_context_and_font = TRUE;
+
+ if (_is_current (SCALED_FONT, scaled_font, 0) &&
+ _is_current (CONTEXT, cr, 1))
+ {
+ if (obj->defined) {
+ _consume_operand (false);
+ } else {
+ _trace_printf ("exch 1 index ");
+ _exch_operands ();
+ }
+ need_context_and_font = FALSE;
+ }
+ else if (_is_current (SCALED_FONT, scaled_font, 1) &&
+ _is_current (CONTEXT, cr, 0))
+ {
+ if (obj->defined) {
+ _trace_printf ("exch ");
+ _exch_operands ();
+ _consume_operand (false);
+ need_context_and_font = FALSE;
+ }
+ }
+
+ if (need_context_and_font) {
+ _emit_context (cr);
+ _emit_scaled_font_id (scaled_font);
+ }
+
+ _trace_printf ("set-scaled-font\n");
+
+ _write_unlock ();
+ }
+ DLCALL (cairo_set_scaled_font, cr, scaled_font);
+ _exit_trace ();
+}
+
+static void
+_emit_matrix (const cairo_matrix_t *m)
+{
+ if (_matrix_is_identity(m))
+ {
+ _trace_printf ("identity");
+ }
+ else
+ {
+ _trace_printf ("%g %g %g %g %g %g matrix",
+ m->xx, m->yx,
+ m->xy, m->yy,
+ m->x0, m->y0);
+ }
+}
+
+cairo_scaled_font_t *
+cairo_scaled_font_create (cairo_font_face_t *font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options)
+{
+ cairo_scaled_font_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_scaled_font_create, font_face, font_matrix, ctm, options);
+ if (_has_scaled_font_id (ret))
+ goto out;
+
+ _emit_line_info ();
+ if (font_face != NULL &&
+ font_matrix != NULL &&
+ ctm != NULL &&
+ options != NULL
+ && _write_lock ())
+ {
+ Object *obj;
+
+ obj = _type_object_create (SCALED_FONT, ret);
+ DLCALL (cairo_scaled_font_set_user_data,
+ ret, &destroy_key, obj, _object_undef);
+
+ if (_pop_operands_to (FONT_FACE, font_face))
+ _consume_operand (false);
+ else
+ _trace_printf ("f%ld ", _get_font_face_id (font_face));
+
+ _emit_matrix (font_matrix);
+ _trace_printf (" ");
+
+ _emit_matrix (ctm);
+ _trace_printf (" ");
+
+ _emit_font_options (options);
+
+ _trace_printf (" scaled-font /sf%ld exch def\n",
+ obj->token);
+ obj->defined = TRUE;
+
+ _write_unlock ();
+ }
+
+out:
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_show_text (cairo_t *cr, const char *utf8)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && _write_lock ()) {
+ _emit_context (cr);
+ _emit_string_literal (utf8, -1);
+ _trace_printf (" show-text\n");
+ _write_unlock ();
+ }
+ DLCALL (cairo_show_text, cr, utf8);
+ _exit_trace ();
+}
+
+static void
+_glyph_advance (cairo_scaled_font_t *font,
+ const cairo_glyph_t *glyph,
+ double *x, double *y)
+{
+ cairo_text_extents_t extents;
+
+ DLCALL (cairo_scaled_font_glyph_extents, font, glyph, 1, &extents);
+ *x += extents.x_advance;
+ *y += extents.y_advance;
+}
+
+#define TOLERANCE 1e-5
+static void
+_emit_glyphs (cairo_scaled_font_t *font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ double x,y;
+ int n;
+
+ if (num_glyphs == 0) {
+ _trace_printf ("[]");
+ return;
+ }
+
+ for (n = 0; n < num_glyphs; n++) {
+ if (glyphs[n].index > 255)
+ break;
+ }
+
+ x = glyphs->x;
+ y = glyphs->y;
+ if (n < num_glyphs) { /* need full glyph range */
+ cairo_bool_t first;
+
+ _trace_printf ("[%g %g [", x, y);
+ first = TRUE;
+ while (num_glyphs--) {
+ if (fabs (glyphs->x - x) > TOLERANCE ||
+ fabs (glyphs->y - y) > TOLERANCE)
+ {
+ x = glyphs->x;
+ y = glyphs->y;
+ _trace_printf ("] %g %g [", x, y);
+ first = TRUE;
+ }
+
+ if (! first)
+ _trace_printf (" ");
+ _trace_printf ("%lu", glyphs->index);
+ first = FALSE;
+
+ _glyph_advance (font, glyphs, &x, &y);
+ glyphs++;
+ }
+ _trace_printf ("]]");
+ } else {
+ struct _data_stream stream;
+
+ if (num_glyphs == 1) {
+ _trace_printf ("[%g %g <%02lx>]", x, y, glyphs->index);
+ } else {
+ _trace_printf ("[%g %g <~", x, y);
+ _write_base85_data_start (&stream);
+ while (num_glyphs--) {
+ unsigned char c;
+
+ if (fabs (glyphs->x - x) > TOLERANCE ||
+ fabs (glyphs->y - y) > TOLERANCE)
+ {
+ x = glyphs->x;
+ y = glyphs->y;
+ _write_base85_data_end (&stream);
+ _trace_printf ("~> %g %g <~", x, y);
+ _write_base85_data_start (&stream);
+ }
+
+ c = glyphs->index;
+ _write_base85_data (&stream, &c, 1);
+
+ _glyph_advance (font, glyphs, &x, &y);
+ glyphs++;
+ }
+ _write_base85_data_end (&stream);
+ _trace_printf ("~>]");
+ }
+ }
+}
+
+void
+cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && glyphs != NULL && _write_lock ()) {
+ cairo_scaled_font_t *font;
+
+ _emit_context (cr);
+ font = DLCALL (cairo_get_scaled_font, cr);
+
+ _emit_glyphs (font, glyphs, num_glyphs);
+ _trace_printf (" show-glyphs\n");
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_show_glyphs, cr, glyphs, num_glyphs);
+ _exit_trace ();
+}
+
+static const char *
+_direction_to_string (cairo_bool_t backward)
+{
+ const char *names[] = {
+ "FORWARD",
+ "BACKWARD"
+ };
+ return names[!!backward];
+}
+
+void
+cairo_show_text_glyphs (cairo_t *cr,
+ const char *utf8,
+ int utf8_len,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t backward)
+{
+ cairo_scaled_font_t *font;
+
+ _enter_trace ();
+
+ font = DLCALL (cairo_get_scaled_font, cr);
+
+ _emit_line_info ();
+ if (cr != NULL && glyphs != NULL && clusters != NULL && _write_lock ()) {
+ int n;
+
+ _emit_context (cr);
+
+ _emit_string_literal (utf8, utf8_len);
+
+ _emit_glyphs (font, glyphs, num_glyphs);
+ _trace_printf (" [");
+ for (n = 0; n < num_clusters; n++) {
+ _trace_printf (" %d %d",
+ clusters[n].num_bytes,
+ clusters[n].num_glyphs);
+ }
+ _trace_printf (" ] //%s show-text-glyphs\n",
+ _direction_to_string (backward));
+
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_show_text_glyphs, cr,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ backward);
+ _exit_trace ();
+}
+
+void
+cairo_text_path (cairo_t *cr, const char *utf8)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (cr != NULL && _write_lock ()) {
+ _emit_context (cr);
+ _emit_string_literal (utf8, -1);
+ _trace_printf (" text-path\n");
+ _write_unlock ();
+ }
+ DLCALL (cairo_text_path, cr, utf8);
+ _exit_trace ();
+}
+
+void
+cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
+{
+ cairo_scaled_font_t *font;
+
+ _enter_trace ();
+
+ font = DLCALL (cairo_get_scaled_font, cr);
+
+ _emit_line_info ();
+ if (cr != NULL && glyphs != NULL && _write_lock ()) {
+ _emit_context (cr);
+ _emit_glyphs (font, glyphs, num_glyphs);
+ _trace_printf (" glyph-path\n");
+
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_glyph_path, cr, glyphs, num_glyphs);
+ _exit_trace ();
+}
+
+void
+cairo_append_path (cairo_t *cr, const cairo_path_t *path)
+{
+ /* XXX no support for named paths, so manually reconstruct */
+ int i;
+ cairo_path_data_t *p;
+
+ _enter_trace ();
+
+ _emit_line_info ();
+ if (cr == NULL || path == NULL) {
+ DLCALL (cairo_append_path, cr, path);
+ _exit_trace ();
+ return;
+ }
+
+ for (i=0; i < path->num_data; i += path->data[i].header.length) {
+ p = &path->data[i];
+ switch (p->header.type) {
+ case CAIRO_PATH_MOVE_TO:
+ if (p->header.length >= 2)
+ cairo_move_to (cr, p[1].point.x, p[1].point.y);
+ break;
+ case CAIRO_PATH_LINE_TO:
+ if (p->header.length >= 2)
+ cairo_line_to (cr, p[1].point.x, p[1].point.y);
+ break;
+ case CAIRO_PATH_CURVE_TO:
+ if (p->header.length >= 4)
+ cairo_curve_to (cr,
+ p[1].point.x, p[1].point.y,
+ p[2].point.x, p[2].point.y,
+ p[3].point.x, p[3].point.y);
+ break;
+ case CAIRO_PATH_CLOSE_PATH:
+ if (p->header.length >= 1)
+ cairo_close_path (cr);
+ break;
+ default:
+ break;
+ }
+ }
+ _exit_trace ();
+}
+
+cairo_surface_t *
+cairo_image_surface_create (cairo_format_t format, int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_image_surface_create, format, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+ const char *format_str = _format_to_string (format);
+ const char *content_str = _format_to_content_string (format);
+
+ _trace_printf ("dict\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " /format //%s set\n"
+ " /content //%s set\n"
+ " image dup /s%ld exch def\n",
+ width, height, format_str, content_str, obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, int height, int stride)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_image_surface_create_for_data, data, format, width, height, stride);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ /* cairo_image_surface_create_for_data() is both used to supply
+ * foreign pixel data to cairo and in order to read pixels back.
+ * Defer grabbing the pixel contents until we have to, but only for
+ * "large" images, for small images the overhead of embedding pixels
+ * is negligible.
+ *
+ * Choose 32x32 as that captures most icons which thanks to GdkPixbuf
+ * are frequently reloaded.
+ */
+ if (width * height < 32*32) {
+ _emit_image (ret, NULL);
+ _trace_printf (" dup /s%ld exch def\n",
+ obj->token);
+ } else {
+ _trace_printf ("dict\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " /format //%s set\n"
+ " image dup /s%ld exch def\n",
+ width, height,
+ _format_to_string (format),
+ obj->token);
+
+ obj->foreign = TRUE;
+ }
+
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+unsigned char *
+cairo_image_surface_get_data (cairo_surface_t *surface)
+{
+ unsigned char *ptr;
+
+ /* Just leave some breadcrumbs */
+ _enter_trace ();
+ _emit_line_info ();
+ if (surface != NULL && _write_lock ()) {
+ _trace_printf ("%% s%ld get-data\n", _get_surface_id (surface));
+ _write_unlock ();
+ }
+ ptr = DLCALL (cairo_image_surface_get_data, surface);
+ _exit_trace ();
+
+ return ptr;
+}
+
+cairo_pattern_t *
+cairo_pattern_create_raster_source (void *data, cairo_content_t content, int width, int height)
+{
+ cairo_pattern_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pattern_create_raster_source, data, content, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ long pattern_id = _create_pattern_id (ret);
+ cairo_format_t format;
+ cairo_surface_t *image;
+ cairo_t *cr;
+
+ /* Impossible to accurately record the interaction with this custom
+ * pattern so just suck all the data into an image upfront */
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA: format = CAIRO_FORMAT_A8; break;
+ case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break;
+ default:
+ case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break;
+ }
+
+ _trace_printf ("%% raster-source\n");
+
+ image = DLCALL (cairo_image_surface_create, format, width, height);
+ cr = DLCALL (cairo_create, image);
+ DLCALL (cairo_set_source, cr, ret);
+ DLCALL (cairo_paint, cr);
+ DLCALL (cairo_destroy, cr);
+
+ _emit_image (image, NULL);
+ DLCALL (cairo_surface_destroy, image);
+ _trace_printf (" pattern dup /s%ld exch def\n",
+ pattern_id);
+
+ _push_operand (PATTERN, ret);
+ _get_object (PATTERN, ret)->defined = TRUE;
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t *other,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_surface_create_similar, other, content, width, height);
+
+ _emit_line_info ();
+ if (other != NULL && _write_lock ()) {
+ Object *other_obj = _get_object(SURFACE, other);
+ Object *new_obj = _create_surface (ret);
+
+ if (other_obj->operand != -1) {
+ if (current_stack_depth == other_obj->operand + 1)
+ _trace_printf ("dup ");
+ else
+ _trace_printf ("%d index ",
+ current_stack_depth - other_obj->operand - 1);
+ } else {
+ assert(other_obj->defined);
+ _trace_printf ("s%ld ", other_obj->token);
+ }
+
+ _trace_printf ("%d %d //%s similar dup /s%ld exch def\n",
+ width, height,
+ _content_to_string (content),
+ new_obj->token);
+
+ new_obj->width = width;
+ new_obj->height = height;
+ new_obj->defined = TRUE;
+
+ _push_object (new_obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_surface_create_similar_image (cairo_surface_t *other,
+ cairo_format_t format,
+ int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_surface_create_similar_image,
+ other, format, width, height);
+
+ _emit_line_info ();
+ if (other != NULL && _write_lock ()) {
+ Object *other_obj = _get_object(SURFACE, other);
+ Object *new_obj = _create_surface (ret);
+
+ if (other_obj->defined)
+ _trace_printf ("s%ld ", other_obj->token);
+ else if (current_stack_depth == other_obj->operand + 1)
+ _trace_printf ("dup ");
+ else
+ _trace_printf ("%d index ",
+ current_stack_depth - other_obj->operand - 1);
+ _trace_printf ("//%s %d %d similar-image %% s%ld\n",
+ _format_to_string (format),
+ width, height,
+ new_obj->token);
+ new_obj->width = width;
+ new_obj->height = height;
+
+ _push_object (new_obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_surface_map_to_image (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_surface_map_to_image, surface, extents);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _emit_surface (surface);
+ if (extents) {
+ _trace_printf ("[%d %d %d %d] map-to-image %% s%ld\n",
+ extents->x, extents->y,
+ extents->width, extents->height,
+ obj->token);
+ obj->width = extents->width;
+ obj->height = extents->height;
+ } else {
+ _trace_printf ("[ ] map-to-image %% s%ld\n", obj->token);
+ }
+
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_surface_unmap_image (cairo_surface_t *surface,
+ cairo_surface_t *image)
+{
+ _enter_trace ();
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *s = _get_object (SURFACE, surface);
+ Object *i = _get_object (SURFACE, image);
+ if (!(s->operand == current_stack_depth - 2 &&
+ i->operand == current_stack_depth - 1)) {
+ if (i->operand != s->operand + 1 || ! _pop_operands_to_depth (i->operand + 1)) {
+ _emit_surface (surface);
+ _emit_surface (image);
+ }
+ }
+ _trace_printf ("unmap-image\n");
+ _consume_operand (true);
+ _write_unlock ();
+ }
+
+ DLCALL (cairo_surface_unmap_image, surface, image);
+
+ _exit_trace ();
+}
+
+cairo_surface_t *
+cairo_surface_create_for_rectangle (cairo_surface_t *target,
+ double x, double y,
+ double width, double height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_surface_create_for_rectangle, target, x, y, width, height);
+
+ _emit_line_info ();
+ if (target != NULL && _write_lock ()) {
+ Object *target_obj = _get_object (SURFACE, target);
+ Object *child_obj = _create_surface (ret);
+
+ if (target_obj->defined)
+ _trace_printf ("s%ld ", target_obj->token);
+ else if (current_stack_depth == target_obj->operand + 1)
+ _trace_printf ("dup ");
+ else
+ _trace_printf ("%d index ", current_stack_depth - target_obj->operand - 1);
+ _trace_printf ("%f %f %f %f subsurface %% s%ld\n",
+ x, y, width, height,
+ child_obj->token);
+
+ _push_object (child_obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+static void CAIRO_PRINTF_FORMAT(2, 3)
+_emit_surface_op (cairo_surface_t *surface, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (surface == NULL || ! _write_lock ())
+ return;
+
+ _emit_surface (surface);
+
+ va_start (ap, fmt);
+ _trace_vprintf ( fmt, ap);
+ va_end (ap);
+
+ _write_unlock ();
+}
+
+void
+cairo_surface_finish (cairo_surface_t *surface)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ DLCALL (cairo_surface_finish, surface);
+ _exit_trace ();
+}
+
+void
+cairo_surface_flush (cairo_surface_t *surface)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (surface != NULL && _write_lock ()) {
+ _trace_printf ("%% s%ld flush\n", _get_surface_id (surface));
+ _write_unlock ();
+ }
+ DLCALL (cairo_surface_flush, surface);
+ _exit_trace ();
+}
+
+void
+cairo_surface_mark_dirty (cairo_surface_t *surface)
+{
+ _enter_trace ();
+ _emit_line_info ();
+
+ /* Call cairo before emitting the trace since _emit_surface() might cause
+ * snapshots to be creates while mark_dirty assert()s that there are none.
+ */
+ DLCALL (cairo_surface_mark_dirty, surface);
+
+ if (surface != NULL && _write_lock ()) {
+ if (_mark_dirty) {
+ _emit_surface (surface);
+ _trace_printf ("%% mark-dirty\n");
+ _emit_source_image (surface);
+ } else
+ _trace_printf ("%% s%ld mark-dirty\n", _get_surface_id (surface));
+ _write_unlock ();
+ }
+ _exit_trace ();
+}
+
+void
+cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
+ int x, int y, int width, int height)
+{
+ _enter_trace ();
+
+ /* Call cairo before emitting the trace since _emit_surface() might cause
+ * snapshots to be creates while mark_dirty assert()s that there are none.
+ */
+ DLCALL (cairo_surface_mark_dirty_rectangle, surface, x, y, width, height);
+
+ _emit_line_info ();
+ if (surface != NULL && _write_lock ()) {
+ if (_mark_dirty) {
+ _emit_surface (surface);
+ _trace_printf ("%% %d %d %d %d mark-dirty-rectangle\n",
+ x, y, width, height);
+ _emit_source_image_rectangle (surface, x,y, width, height);
+ } else
+ _trace_printf ("%% s%ld %d %d %d %d mark-dirty-rectangle\n",
+ _get_surface_id (surface), x, y, width, height);
+ _write_unlock ();
+ }
+ _exit_trace ();
+}
+
+void
+cairo_surface_set_device_offset (cairo_surface_t *surface, double x_offset, double y_offset)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_surface_op (surface, "%g %g set-device-offset\n",
+ x_offset, y_offset);
+ DLCALL (cairo_surface_set_device_offset, surface, x_offset, y_offset);
+ _exit_trace ();
+}
+
+void
+cairo_surface_set_device_scale (cairo_surface_t *surface, double x_offset, double y_offset)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_surface_op (surface, "%g %g set-device-scale\n",
+ x_offset, y_offset);
+ DLCALL (cairo_surface_set_device_scale, surface, x_offset, y_offset);
+ _exit_trace ();
+}
+
+
+void
+cairo_surface_set_fallback_resolution (cairo_surface_t *surface, double x_pixels_per_inch, double y_pixels_per_inch)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_surface_op (surface, "%g %g set-fallback-resolution\n",
+ x_pixels_per_inch, y_pixels_per_inch);
+ DLCALL (cairo_surface_set_fallback_resolution, surface, x_pixels_per_inch, y_pixels_per_inch);
+ _exit_trace ();
+}
+
+void
+cairo_surface_copy_page (cairo_surface_t *surface)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_surface_op (surface, "copy-page\n");
+ DLCALL (cairo_surface_copy_page, surface);
+ _exit_trace ();
+}
+
+void
+cairo_surface_show_page (cairo_surface_t *surface)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_surface_op (surface, "show-page\n");
+ DLCALL (cairo_surface_show_page, surface);
+ _exit_trace ();
+}
+
+cairo_status_t
+cairo_surface_set_mime_data (cairo_surface_t *surface,
+ const char *mime_type,
+ const unsigned char *data,
+ unsigned long length,
+ cairo_destroy_func_t destroy,
+ void *closure)
+{
+ cairo_status_t ret;
+ _enter_trace ();
+ _emit_line_info ();
+ if (surface != NULL && _write_lock ()) {
+ _emit_surface (surface);
+ _emit_string_literal (mime_type, -1);
+ _trace_printf (" ");
+ _emit_data (data, length);
+ _trace_printf (" /deflate filter set-mime-data\n");
+
+ _write_unlock ();
+ }
+
+ ret = DLCALL (cairo_surface_set_mime_data,
+ surface,
+ mime_type,
+ data, length,
+ destroy,
+ closure);
+ _exit_trace ();
+ return ret;
+}
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+cairo_status_t
+cairo_surface_write_to_png (cairo_surface_t *surface, const char *filename)
+{
+ cairo_status_t ret;
+ _enter_trace ();
+ _emit_line_info ();
+ if (surface != NULL && _write_lock ()) {
+ _trace_printf ("%% s%ld ", _get_surface_id (surface));
+ _emit_string_literal (filename, -1);
+ _trace_printf (" write-to-png pop\n");
+ _write_unlock ();
+ }
+ ret = DLCALL (cairo_surface_write_to_png, surface, filename);
+ _exit_trace ();
+ return ret;
+}
+
+cairo_status_t
+cairo_surface_write_to_png_stream (cairo_surface_t *surface,
+ cairo_write_func_t write_func,
+ void *data)
+{
+ cairo_status_t ret;
+ _enter_trace ();
+ _emit_line_info ();
+ if (surface != NULL && _write_lock ()) {
+ char symbol[1024];
+
+ _trace_printf ("%% s%ld ", _get_surface_id (surface));
+#if CAIRO_HAS_SYMBOL_LOOKUP
+ lookup_symbol (symbol, sizeof (symbol), write_func);
+#else
+ symbol[0] = '\0';
+#endif
+ _emit_string_literal (symbol, -1);
+ _trace_printf (" write-to-png-stream pop\n");
+ _write_unlock ();
+ }
+ ret = DLCALL (cairo_surface_write_to_png_stream,
+ surface, write_func, data);
+ _exit_trace ();
+ return ret;
+}
+#endif
+
+static void CAIRO_PRINTF_FORMAT(2, 3)
+_emit_pattern_op (cairo_pattern_t *pattern, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (pattern == NULL || ! _write_lock ())
+ return;
+
+ _emit_pattern (pattern);
+
+ va_start (ap, fmt);
+ _trace_vprintf (fmt, ap);
+ va_end (ap);
+
+ _write_unlock ();
+}
+
+cairo_pattern_t *
+cairo_pattern_create_rgb (double red, double green, double blue)
+{
+ cairo_pattern_t *ret;
+ long pattern_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pattern_create_rgb, red, green, blue);
+ pattern_id = _create_pattern_id (ret);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ _trace_printf ("/p%ld %g %g %g rgb def\n",
+ pattern_id, red, green, blue);
+ _get_object (PATTERN, ret)->defined = TRUE;
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_pattern_t *
+cairo_pattern_create_rgba (double red, double green, double blue, double alpha)
+{
+ cairo_pattern_t *ret;
+ long pattern_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pattern_create_rgba, red, green, blue, alpha);
+ pattern_id = _create_pattern_id (ret);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ _trace_printf ("/p%ld %g %g %g %g rgba def\n",
+ pattern_id, red, green, blue, alpha);
+ _get_object (PATTERN, ret)->defined = TRUE;
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_pattern_t *
+cairo_pattern_create_for_surface (cairo_surface_t *surface)
+{
+ cairo_pattern_t *ret;
+ long pattern_id;
+ long surface_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pattern_create_for_surface, surface);
+ pattern_id = _create_pattern_id (ret);
+
+ _emit_line_info ();
+ if (surface != NULL && _write_lock ()) {
+ surface_id = _get_surface_id (surface);
+
+ if (_pop_operands_to (SURFACE, surface)) {
+ _consume_operand (false);
+ } else {
+ _trace_printf ("s%ld ", surface_id);
+ }
+
+ if (_get_object (SURFACE, surface)->foreign)
+ _emit_source_image (surface);
+
+ _trace_printf ("pattern %% p%ld\n", pattern_id);
+ _push_operand (PATTERN, ret);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_pattern_t *
+cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
+{
+ cairo_pattern_t *ret;
+ long pattern_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pattern_create_linear, x0, y0, x1, y1);
+ pattern_id = _create_pattern_id (ret);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ _trace_printf ("%g %g %g %g linear %% p%ld\n",
+ x0, y0, x1, y1, pattern_id);
+ _push_operand (PATTERN, ret);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_pattern_t *
+cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1)
+{
+ cairo_pattern_t *ret;
+ long pattern_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pattern_create_radial,
+ cx0, cy0, radius0,
+ cx1, cy1, radius1);
+ pattern_id = _create_pattern_id (ret);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ _trace_printf ("%g %g %g %g %g %g radial %% p%ld\n",
+ cx0, cy0, radius0, cx1, cy1, radius1,
+ pattern_id);
+ _push_operand (PATTERN, ret);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, double offset, double red, double green, double blue)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_pattern_op (pattern,
+ "%g %g %g %g 1 add-color-stop\n",
+ offset, red, green, blue);
+ DLCALL (cairo_pattern_add_color_stop_rgb, pattern, offset, red, green, blue);
+ _exit_trace ();
+}
+
+void
+cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, double offset, double red, double green, double blue, double alpha)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_pattern_op (pattern,
+ "%g %g %g %g %g add-color-stop\n",
+ offset, red, green, blue, alpha);
+ DLCALL (cairo_pattern_add_color_stop_rgba, pattern, offset, red, green, blue, alpha);
+ _exit_trace ();
+}
+
+void
+cairo_pattern_set_matrix (cairo_pattern_t *pattern, const cairo_matrix_t *matrix)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ if (_matrix_is_identity (matrix)) {
+ _emit_pattern_op (pattern, "identity set-matrix\n");
+ } else {
+ _emit_pattern_op (pattern,
+ "%g %g %g %g %g %g matrix set-matrix\n",
+ matrix->xx, matrix->yx,
+ matrix->xy, matrix->yy,
+ matrix->x0, matrix->y0);
+ }
+ DLCALL (cairo_pattern_set_matrix, pattern, matrix);
+ _exit_trace ();
+}
+
+static const char *
+_filter_to_string (cairo_filter_t filter)
+{
+#define f(name) case CAIRO_FILTER_ ## name: return "FILTER_" #name
+ switch (filter) {
+ f(FAST);
+ f(GOOD);
+ f(BEST);
+ f(NEAREST);
+ f(BILINEAR);
+ f(GAUSSIAN);
+ };
+#undef f
+ return "UNKNOWN_FILTER";
+}
+
+void
+cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_pattern_op (pattern, "//%s set-filter\n", _filter_to_string (filter));
+ DLCALL (cairo_pattern_set_filter, pattern, filter);
+ _exit_trace ();
+}
+
+static const char *
+_extend_to_string (cairo_extend_t extend)
+{
+#define f(name) case CAIRO_EXTEND_ ## name: return "EXTEND_" #name
+ switch (extend) {
+ f(NONE);
+ f(REPEAT);
+ f(REFLECT);
+ f(PAD);
+ };
+#undef f
+ return "UNKNOWN_EXTEND";
+}
+
+void
+cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ _emit_pattern_op (pattern, "//%s set-extend\n", _extend_to_string (extend));
+ DLCALL (cairo_pattern_set_extend, pattern, extend);
+ _exit_trace ();
+}
+
+#if CAIRO_HAS_FT_FONT
+#if CAIRO_HAS_FC_FONT
+cairo_font_face_t *
+cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
+{
+ cairo_font_face_t *ret;
+ long font_face_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_ft_font_face_create_for_pattern, pattern);
+ font_face_id = _create_font_face_id (ret);
+
+ _emit_line_info ();
+ if (pattern != NULL && _write_lock ()) {
+ Object *obj;
+
+ obj = _get_object (FONT_FACE, ret);
+ if (obj->unknown) {
+ FcPattern *copy;
+ FcChar8 *unparsed;
+
+ copy = DLCALL (FcPatternDuplicate, pattern);
+ if (copy)
+ {
+ DLCALL (FcPatternDel, copy, FC_LANG);
+ DLCALL (FcPatternDel, copy, FC_CHARSET);
+ DLCALL (FcPatternDel, copy, FC_CAPABILITY);
+ }
+ else
+ copy = pattern;
+
+ unparsed = DLCALL (FcNameUnparse, copy);
+ _trace_printf ("dict\n"
+ " /type 42 set\n"
+ " /pattern ");
+ _emit_string_literal ((char *) unparsed, -1);
+ _trace_printf (" set\n"
+ " font %% f%ld\n",
+ font_face_id);
+ obj->unknown = FALSE;
+ _push_operand (FONT_FACE, ret);
+ dump_stack(__func__);
+
+ if (copy != pattern)
+ DLCALL (FcPatternDestroy, copy);
+ free (unparsed);
+ }
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+#endif /* CAIRO_HAS_FC_FONT*/
+
+typedef struct _ft_face_data {
+ unsigned long index;
+ unsigned long size;
+ void *data;
+} FtFaceData;
+
+static void
+_ft_face_data_destroy (void *arg)
+{
+ FtFaceData *data = arg;
+ free (data->data);
+ free (data);
+}
+
+cairo_font_face_t *
+cairo_ft_font_face_create_for_ft_face (FT_Face face, int load_flags)
+{
+ cairo_font_face_t *ret;
+ Object *obj;
+ FtFaceData *data;
+ long font_face_id;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_ft_font_face_create_for_ft_face, face, load_flags);
+ font_face_id = _create_font_face_id (ret);
+
+ if (face == NULL) {
+ _exit_trace ();
+ return ret;
+ }
+
+ obj = _get_object (NONE, face);
+ data = obj->data;
+ if (data == NULL) {
+ _exit_trace ();
+ return ret;
+ }
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ obj = _get_object (FONT_FACE, ret);
+ if (obj->operand != -1)
+ _object_remove (obj);
+
+ _trace_printf ("<< /type 42 /source ");
+ _emit_data (data->data, data->size);
+ _trace_printf (" /index %lu /flags %d >> font %% f%ld\n",
+ data->index, load_flags, font_face_id);
+ _push_operand (FONT_FACE, ret);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+static cairo_bool_t
+_ft_read_file (FtFaceData *data, const char *path)
+{
+ char buf[8192];
+ FILE *file;
+
+ file = fopen (path, "rb");
+ if (file != NULL) {
+ size_t ret;
+ unsigned long int allocated = sizeof (buf);
+ data->data = malloc (allocated);
+ do {
+ ret = fread (buf, 1, sizeof (buf), file);
+ if (ret == 0)
+ break;
+ memcpy ((char *) data->data + data->size, buf, ret);
+ data->size += ret;
+ if (ret != sizeof (buf))
+ break;
+
+ if (data->size == allocated) {
+ allocated *= 2;
+ data->data = realloc (data->data, allocated);
+ }
+ } while (TRUE);
+ fclose (file);
+ }
+
+ return file != NULL;
+}
+
+FT_Error
+FT_New_Face (FT_Library library, const char *pathname, FT_Long index, FT_Face *face)
+{
+ FT_Error ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (FT_New_Face, library, pathname, index, face);
+ if (ret == 0) {
+ Object *obj = _type_object_create (NONE, *face);
+ FtFaceData *data = malloc (sizeof (FtFaceData));
+ data->index = index;
+ data->size = 0;
+ data->data = NULL;
+ _ft_read_file (data, pathname);
+ obj->data = data;
+ obj->destroy = _ft_face_data_destroy;
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+FT_Error
+FT_New_Memory_Face (FT_Library library, const FT_Byte *mem, FT_Long size, FT_Long index, FT_Face *face)
+{
+ FT_Error ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (FT_New_Memory_Face, library, mem, size, index, face);
+ if (ret == 0) {
+ Object *obj = _type_object_create (NONE, *face);
+ FtFaceData *data = malloc (sizeof (FtFaceData));
+ data->index = index;
+ data->size = size;
+ data->data = malloc (size);
+ memcpy (data->data, mem, size);
+ obj->data = data;
+ obj->destroy = _ft_face_data_destroy;
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+/* XXX
+ * FT_New_Memory_Face() and FT_New_Face() appear to wrap FT_Open_Face() so we
+ * get a redundant call to FT_Open_Face() from those paths (no PLT hiding
+ * within FT, naughty library!) but we do not intercept a direct call to
+ * FT_Open_Face(). So far this has not caused any issues, but it will one
+ * day...
+ */
+FT_Error
+FT_Open_Face (FT_Library library, const FT_Open_Args *args, FT_Long index, FT_Face *face)
+{
+ FT_Error ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (FT_Open_Face, library, args, index, face);
+ if (ret == 0) {
+ Object *obj = _get_object (NONE, *face);
+ if (obj == NULL) {
+ FtFaceData *data;
+
+ data = malloc (sizeof (FtFaceData));
+ data->index = index;
+ if (args->flags & FT_OPEN_MEMORY) {
+ data->size = args->memory_size;
+ data->data = malloc (args->memory_size);
+ memcpy (data->data, args->memory_base, args->memory_size);
+ } else if (args->flags & FT_OPEN_STREAM) {
+ fprintf (stderr, "FT_Open_Face (stream, %ld) = %p\n",
+ index, *face);
+ abort ();
+ } else if (args->flags & FT_OPEN_PATHNAME) {
+ data->size = 0;
+ data->data = NULL;
+ _ft_read_file (data, args->pathname);
+ }
+
+ obj = _type_object_create (NONE, *face);
+ obj->data = data;
+ obj->destroy = _ft_face_data_destroy;
+ }
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+FT_Error
+FT_Done_Face (FT_Face face)
+{
+ FT_Error ret;
+ _enter_trace ();
+
+ _object_destroy (_get_object (NONE, face));
+
+ ret = DLCALL (FT_Done_Face, face);
+ _exit_trace ();
+ return ret;
+}
+#endif
+
+static void
+_surface_object_set_size (cairo_surface_t *surface, int width, int height)
+{
+ Object *obj;
+
+ obj = _get_object (SURFACE, surface);
+ obj->width = width;
+ obj->height = height;
+}
+
+static void
+_surface_object_set_size_from_surface (cairo_surface_t *surface)
+{
+ _surface_object_set_size (surface,
+ DLCALL (cairo_image_surface_get_width, surface),
+ DLCALL (cairo_image_surface_get_height, surface));
+}
+
+#if CAIRO_HAS_PS_SURFACE
+#include<cairo-ps.h>
+
+cairo_surface_t *
+cairo_ps_surface_create (const char *filename, double width_in_points, double height_in_points)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_ps_surface_create, filename, width_in_points, height_in_points);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /PS set\n"
+ " /filename ");
+ _emit_string_literal (filename, -1);
+ _trace_printf (" set\n"
+ " /width %g set\n"
+ " /height %g set\n"
+ " surface %% s%ld\n",
+ width_in_points,
+ height_in_points,
+ obj->token);
+ obj->width = width_in_points;
+ obj->height = height_in_points;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_ps_surface_create_for_stream, write_func, closure, width_in_points, height_in_points);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /PS set\n"
+ " /width %g set\n"
+ " /height %g set\n"
+ " surface %% s%ld\n",
+ width_in_points,
+ height_in_points,
+ obj->token);
+ obj->width = width_in_points;
+ obj->height = height_in_points;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_ps_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ DLCALL (cairo_ps_surface_set_size, surface, width_in_points, height_in_points);
+ _exit_trace ();
+}
+
+#endif
+
+#if CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+
+cairo_surface_t *
+cairo_pdf_surface_create (const char *filename, double width_in_points, double height_in_points)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pdf_surface_create, filename, width_in_points, height_in_points);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /PDF set\n"
+ " /filename ");
+ _emit_string_literal (filename, -1);
+ _trace_printf (" set\n"
+ " /width %g set\n"
+ " /height %g set\n"
+ " surface %% s%ld\n",
+ width_in_points,
+ height_in_points,
+ obj->token);
+ obj->width = width_in_points;
+ obj->height = height_in_points;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_pdf_surface_create_for_stream, write_func, closure, width_in_points, height_in_points);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /PDF set\n"
+ " /width %g set\n"
+ " /height %g set\n"
+ " surface %% s%ld\n",
+ width_in_points,
+ height_in_points,
+ obj->token);
+ obj->width = width_in_points;
+ obj->height = height_in_points;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+ _exit_trace ();
+ return ret;
+}
+
+void
+cairo_pdf_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points)
+{
+ _enter_trace ();
+ _emit_line_info ();
+ DLCALL (cairo_pdf_surface_set_size, surface, width_in_points, height_in_points);
+ _exit_trace ();
+}
+#endif
+
+#if CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+
+cairo_surface_t *
+cairo_svg_surface_create (const char *filename, double width, double height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_svg_surface_create, filename, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /SVG set\n"
+ " /filename ");
+ _emit_string_literal (filename, -1);
+ _trace_printf (" set\n"
+ " /width %g set\n"
+ " /height %g set\n"
+ " surface %% s%ld\n",
+ width,
+ height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width, double height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_svg_surface_create_for_stream, write_func, closure, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /SVG set\n"
+ " /width %g set\n"
+ " /height %g set\n"
+ " surface %% s%ld\n",
+ width,
+ height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+#endif
+
+#if CAIRO_HAS_PNG_FUNCTIONS
+cairo_surface_t *
+cairo_image_surface_create_from_png (const char *filename)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_image_surface_create_from_png, filename);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+ char filename_string[4096];
+
+ _encode_string_literal (filename_string, sizeof (filename_string),
+ filename, -1);
+ _emit_image (ret, " /filename %s set\n", filename_string);
+ _trace_printf (" dup /s%ld exch def\n", obj->token);
+ _surface_object_set_size_from_surface (ret);
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, void *closure)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_image_surface_create_from_png_stream, read_func, closure);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _emit_image (ret, NULL);
+ _trace_printf (" dup /s%ld exch def\n",
+ obj->token);
+
+ _surface_object_set_size_from_surface (ret);
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+#endif
+
+static const char *
+_content_from_surface (cairo_surface_t *surface)
+{
+ return _content_to_string (DLCALL (cairo_surface_get_content, surface));
+}
+
+#if CAIRO_HAS_TEE_SURFACE
+#include <cairo-tee.h>
+
+cairo_surface_t *
+cairo_tee_surface_create (cairo_surface_t *master)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_tee_surface_create, master);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /tee set\n"
+ " /master s%ld set\n"
+ " surface dup /s%ld exch def\n",
+ _get_object (SURFACE, master)->token,
+ obj->token);
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+#endif
+
+#if CAIRO_HAS_XLIB_SURFACE
+#include <cairo-xlib.h>
+
+cairo_surface_t *
+cairo_xlib_surface_create (Display *dpy,
+ Drawable drawable,
+ Visual *visual,
+ int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_xlib_surface_create,
+ dpy, drawable, visual, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /xlib set\n"
+ " /drawable 16!%lx set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ drawable,
+ _content_from_surface (ret),
+ width, height,
+ obj->token);
+ obj->defined = TRUE;
+ obj->width = width;
+ obj->height = height;
+ obj->foreign = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_xlib_surface_create_for_bitmap (Display *dpy,
+ Pixmap bitmap,
+ Screen *screen,
+ int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_xlib_surface_create_for_bitmap,
+ dpy, bitmap, screen, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /xlib set\n"
+ " /drawable 16!%lx set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " /depth 1 set\n"
+ " surface dup /s%ld exch def\n",
+ bitmap,
+ _content_from_surface (ret),
+ width, height,
+ obj->token);
+ obj->defined = TRUE;
+ obj->width = width;
+ obj->height = height;
+ obj->foreign = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+#include <cairo-xlib-xrender.h>
+cairo_surface_t *
+cairo_xlib_surface_create_with_xrender_format (Display *dpy,
+ Drawable drawable,
+ Screen *screen,
+ XRenderPictFormat *format,
+ int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_xlib_surface_create_with_xrender_format,
+ dpy, drawable, screen, format, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /xrender set\n"
+ " /drawable 16!%lx set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " /depth %d set\n"
+ " surface dup /s%ld exch def\n",
+ drawable,
+ _content_from_surface (ret),
+ width, height,
+ format->depth,
+ obj->token);
+ obj->defined = TRUE;
+ obj->width = width;
+ obj->height = height;
+ obj->foreign = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+#endif
+#endif
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+#include <cairo-script.h>
+cairo_surface_t *
+cairo_script_surface_create (cairo_device_t *device,
+ cairo_content_t content,
+ double width,
+ double height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_script_surface_create, device, content, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /script set\n"
+ " /content %s set\n"
+ " /width %g set\n"
+ " /height %g set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_device_t *device,
+ cairo_surface_t *target)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_script_surface_create_for_target, device, target);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /script set\n"
+ " surface dup /s%ld exch def\n",
+ obj->token);
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+#endif
+
+#if CAIRO_HAS_TEST_SURFACES
+#include <test-paginated-surface.h>
+cairo_surface_t *
+_cairo_test_paginated_surface_create (cairo_surface_t *surface)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (_cairo_test_paginated_surface_create, surface);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ /* XXX store initial data? */
+ _trace_printf ("dict\n"
+ " /type /test-paginated set\n"
+ " /target s%ld set\n"
+ " surface dup /s%ld exch def\n",
+ _get_surface_id (surface),
+ obj->token);
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+#include <test-compositor-surface.h>
+
+cairo_surface_t *
+_cairo_test_fallback_compositor_surface_create (cairo_content_t content, int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (_cairo_test_fallback_compositor_surface_create, content, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /test-fallback-compositor set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+_cairo_test_mask_compositor_surface_create (cairo_content_t content, int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (_cairo_test_mask_compositor_surface_create, content, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /test-mask-compositor set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+_cairo_test_spans_compositor_surface_create (cairo_content_t content, int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (_cairo_test_spans_compositor_surface_create, content, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /test-spans-compositor set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+_cairo_test_traps_compositor_surface_create (cairo_content_t content, int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (_cairo_test_traps_compositor_surface_create, content, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /test-traps-compositor set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+#endif
+
+cairo_surface_t *
+cairo_recording_surface_create (cairo_content_t content,
+ const cairo_rectangle_t *extents)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_recording_surface_create, content, extents);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ if (extents) {
+ _trace_printf ("//%s [ %f %f %f %f ] record dup /s%ld exch def\n",
+ _content_to_string (content),
+ extents->x, extents->y,
+ extents->width, extents->height,
+ obj->token);
+ obj->width = extents->width;
+ obj->height = extents->height;
+ } else {
+ _trace_printf ("//%s [ ] record dup /s%ld exch def\n",
+ _content_to_string (content),
+ obj->token);
+ }
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+#if CAIRO_HAS_VG_SURFACE
+#include <cairo-vg.h>
+cairo_surface_t *
+cairo_vg_surface_create (cairo_vg_context_t *context,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_vg_surface_create, context, content, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /vg set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
+ VGImage image,
+ VGImageFormat format,
+ int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_vg_surface_create_for_image,
+ context, image, format, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+ cairo_content_t content;
+
+ content = DLCALL (cairo_surface_get_content, ret);
+ _trace_printf ("dict\n"
+ " /type /vg set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+#endif
+
+#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE
+#include <cairo-gl.h>
+cairo_surface_t *
+cairo_gl_surface_create (cairo_device_t *abstract_device,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_gl_surface_create, abstract_device, content, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /gl set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device,
+ cairo_content_t content,
+ unsigned int tex,
+ int width,
+ int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_gl_surface_create_for_texture, abstract_device, content, tex, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /gl set\n"
+ " /content //%s set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ _content_to_string (content),
+ width, height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+cairo_surface_t *
+cairo_gl_surface_create_for_window (cairo_device_t *device,
+ Window win,
+ int width, int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_gl_surface_create_for_window, device, win, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /gl set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ width, height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+#endif
+
+#if CAIRO_HAS_WGL_FUNCTIONS
+cairo_surface_t *
+cairo_gl_surface_create_for_dc (cairo_device_t *device,
+ HDC dc,
+ int width,
+ int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_gl_surface_create_for_dc, device, dc, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /gl set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ width, height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+cairo_surface_t *
+cairo_gl_surface_create_for_egl (cairo_device_t *device,
+ EGLSurface egl,
+ int width,
+ int height)
+{
+ cairo_surface_t *ret;
+
+ _enter_trace ();
+
+ ret = DLCALL (cairo_gl_surface_create_for_egl, device, egl, width, height);
+
+ _emit_line_info ();
+ if (_write_lock ()) {
+ Object *obj = _create_surface (ret);
+
+ _trace_printf ("dict\n"
+ " /type /gl set\n"
+ " /width %d set\n"
+ " /height %d set\n"
+ " surface dup /s%ld exch def\n",
+ width, height,
+ obj->token);
+ obj->width = width;
+ obj->height = height;
+ obj->defined = TRUE;
+ _push_object (obj);
+ dump_stack(__func__);
+ _write_unlock ();
+ }
+
+ _exit_trace ();
+ return ret;
+}
+#endif
+#endif
diff --git a/util/cairo.modules b/util/cairo.modules
index 71a3922fd..71a3922fd 100755..100644
--- a/util/cairo.modules
+++ b/util/cairo.modules
diff --git a/util/font-view.c b/util/font-view.c
index 07d9e2e9d..07d9e2e9d 100755..100644
--- a/util/font-view.c
+++ b/util/font-view.c
diff --git a/util/malloc-stats.c b/util/malloc-stats.c
index da91656dd..55ed51cad 100755..100644
--- a/util/malloc-stats.c
+++ b/util/malloc-stats.c
@@ -60,8 +60,9 @@ static struct alloc_stats_t total_allocations;
static struct func_stat_t *func_stats[31627];
static int func_stats_num;
-#define ARRAY_SIZE(A) (sizeof (A)/sizeof (A[0]))
-
+#ifndef ARRAY_LENGTH
+#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
+#endif
static void
alloc_stats_add (struct alloc_stats_t *stats, int is_realloc, size_t size)
{
@@ -147,7 +148,7 @@ func_stats_add (const void *caller, int is_realloc, size_t size)
alloc_stats_add (&total_allocations, is_realloc, size);
- i = ((uintptr_t) caller ^ 1215497) % ARRAY_SIZE (func_stats);
+ i = ((uintptr_t) caller ^ 1215497) % ARRAY_LENGTH (func_stats);
for (elt = func_stats[i]; elt != NULL; elt = elt->next) {
if (elt->addr == caller)
break;
@@ -328,7 +329,7 @@ malloc_stats (void)
return;
j = 0;
- for (i = 0; i < ARRAY_SIZE (func_stats); i++) {
+ for (i = 0; i < ARRAY_LENGTH (func_stats); i++) {
struct func_stat_t *elt;
for (elt = func_stats[i]; elt != NULL; elt = elt->next)
sorted_func_stats[j++] = *elt;
diff --git a/util/show-contour.c b/util/show-contour.c
index f3fa1babf..f3fa1babf 100755..100644
--- a/util/show-contour.c
+++ b/util/show-contour.c
diff --git a/util/show-edges.c b/util/show-edges.c
index a85ad5f7e..a85ad5f7e 100755..100644
--- a/util/show-edges.c
+++ b/util/show-edges.c
diff --git a/util/show-events.c b/util/show-events.c
index 8bff3efc4..8bff3efc4 100755..100644
--- a/util/show-events.c
+++ b/util/show-events.c
diff --git a/util/show-polygon.c b/util/show-polygon.c
index 35c0014d1..35c0014d1 100755..100644
--- a/util/show-polygon.c
+++ b/util/show-polygon.c
diff --git a/util/show-traps.c b/util/show-traps.c
index f46c8b009..f46c8b009 100755..100644
--- a/util/show-traps.c
+++ b/util/show-traps.c
diff --git a/util/trace-to-xml.c b/util/trace-to-xml.c
index a0f03ccff..a0f03ccff 100755..100644
--- a/util/trace-to-xml.c
+++ b/util/trace-to-xml.c
diff --git a/util/xml-to-trace.c b/util/xml-to-trace.c
index 13b7e5706..13b7e5706 100755..100644
--- a/util/xml-to-trace.c
+++ b/util/xml-to-trace.c